The command module validates args.chdir before honoring check mode, so dry runs fail with ENOENT on the stack dir (file task only pretends to create it). Gate Deploy stack on ansible_check_mode — dry runs should not run docker compose up anyway.
103 lines
No EOL
3.2 KiB
YAML
103 lines
No EOL
3.2 KiB
YAML
- name: Deploy n8n
|
|
hosts: all
|
|
become: true
|
|
|
|
vars:
|
|
stack_dir: "/opt/stacks/{{ stack_name }}"
|
|
|
|
pre_tasks:
|
|
- name: Fail if stack_name is not set
|
|
fail:
|
|
msg: "stack_name is required"
|
|
when: stack_name is not defined or stack_name | length == 0
|
|
|
|
- name: Fail if n8n_url is not set
|
|
fail:
|
|
msg: "n8n_url is required"
|
|
when: n8n_url is not defined or n8n_url | length == 0
|
|
|
|
tasks:
|
|
# 🔎 Check if 1Password item exists
|
|
- name: Check if 1Password item exists
|
|
shell: |
|
|
op item get "{{ stack_name }}" --vault="Automation"
|
|
register: op_check
|
|
failed_when: false
|
|
changed_when: false
|
|
check_mode: no
|
|
environment:
|
|
OP_SERVICE_ACCOUNT_TOKEN: "{{ lookup('env','OP_SERVICE_ACCOUNT_TOKEN') }}"
|
|
|
|
# 🔐 Load existing secrets
|
|
- name: Load secrets from 1Password
|
|
when: op_check.rc == 0
|
|
shell: |
|
|
op item get "{{ stack_name }}" --vault="Automation" --format json
|
|
register: op_item
|
|
check_mode: no
|
|
environment:
|
|
OP_SERVICE_ACCOUNT_TOKEN: "{{ lookup('env','OP_SERVICE_ACCOUNT_TOKEN') }}"
|
|
|
|
- name: Set secrets from 1Password
|
|
when: op_check.rc == 0
|
|
set_fact:
|
|
n8n_postgres_password: "{{ (op_item.stdout | from_json).fields | selectattr('label','equalto','postgres_password') | map(attribute='value') | first }}"
|
|
n8n_encryption_key: "{{ (op_item.stdout | from_json).fields | selectattr('label','equalto','encryption_key') | map(attribute='value') | first }}"
|
|
|
|
# 🔑 Generate if new
|
|
- name: Generate postgres password
|
|
when: op_check.rc != 0
|
|
set_fact:
|
|
n8n_postgres_password: "{{ lookup('password','/dev/null length=32 chars=ascii_letters') }}"
|
|
|
|
- name: Generate encryption key
|
|
when: op_check.rc != 0
|
|
set_fact:
|
|
n8n_encryption_key: "{{ lookup('password','/dev/null length=64 chars=hexdigits') }}"
|
|
|
|
# 📦 Store in 1Password
|
|
- name: Create 1Password item
|
|
when: op_check.rc != 0
|
|
shell: |
|
|
op item create \
|
|
--category="Server" \
|
|
--title="{{ stack_name }}" \
|
|
--vault="Automation" \
|
|
"url=https://{{ n8n_url }}" \
|
|
"username={{ n8n_editor_email }}" \
|
|
"password={{ n8n_editor_password }}" \
|
|
"postgres_password={{ n8n_postgres_password }}" \
|
|
"encryption_key={{ n8n_encryption_key }}"
|
|
environment:
|
|
OP_SERVICE_ACCOUNT_TOKEN: "{{ lookup('env','OP_SERVICE_ACCOUNT_TOKEN') }}"
|
|
|
|
# 🧱 Infrastructure
|
|
- name: Ensure stack directory exists
|
|
file:
|
|
path: "{{ stack_dir }}"
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Ensure pangolin network exists
|
|
community.docker.docker_network:
|
|
name: pangolin
|
|
state: present
|
|
|
|
# 📄 Templates
|
|
- name: Render .env
|
|
template:
|
|
src: templates/n8n.env.j2
|
|
dest: "{{ stack_dir }}/.env"
|
|
mode: "0600"
|
|
|
|
- name: Render compose file
|
|
template:
|
|
src: templates/n8n-compose.yml.j2
|
|
dest: "{{ stack_dir }}/compose.yml"
|
|
|
|
# 🚀 Deploy
|
|
- name: Deploy stack
|
|
command: docker compose up -d
|
|
args:
|
|
chdir: "{{ stack_dir }}"
|
|
when: not ansible_check_mode |