--- - name: Ensure application stack destination directory exists file: path: "{{ application_stack_dest }}" state: directory mode: '0755' - name: Ensure secrets directory exists for Docker Compose secrets file: path: "{{ application_stack_dest }}/secrets" state: directory owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0700' - name: Ensure parent directory exists for application code file: path: "/home/deploy/michaelschiemer" state: directory owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0755' when: application_compose_suffix == 'production.yml' become: yes - name: Ensure application code directory exists file: path: "/home/deploy/michaelschiemer/current" state: directory owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0755' when: application_compose_suffix == 'production.yml' become: yes ignore_errors: yes - name: Fix ownership of application code directory if needed command: chown -R {{ ansible_user }}:{{ ansible_user }} /home/deploy/michaelschiemer/current when: - application_compose_suffix == 'production.yml' - ansible_check_mode is not defined or not ansible_check_mode become: yes changed_when: false failed_when: false - name: Check if vault file exists locally stat: path: "{{ application_vault_file }}" delegate_to: localhost register: application_vault_stat become: no - name: Optionally load application secrets from vault include_vars: file: "{{ application_vault_file }}" when: application_vault_stat.stat.exists no_log: yes ignore_errors: yes delegate_to: localhost become: no - name: Check if PostgreSQL Production .env exists on target host stat: path: "{{ stacks_base_path }}/postgresql-production/.env" register: application_postgres_production_env_file changed_when: false - name: Check if PostgreSQL Staging .env exists on target host (for staging deployments) stat: path: "{{ stacks_base_path }}/postgresql-staging/.env" register: application_postgres_staging_env_file changed_when: false when: application_compose_suffix == 'staging' - name: Extract PostgreSQL Production password from .env file shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql-production/.env 2>/dev/null | cut -d'=' -f2- || echo ''" register: application_postgres_production_password changed_when: false failed_when: false when: application_postgres_production_env_file.stat.exists no_log: yes - name: Extract PostgreSQL Staging password from .env file shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql-staging/.env 2>/dev/null | cut -d'=' -f2- || echo ''" register: application_postgres_staging_password changed_when: false failed_when: false when: - application_compose_suffix == 'staging' - application_postgres_staging_env_file.stat.exists no_log: yes - name: "Fallback: Check if legacy PostgreSQL .env exists on target host" stat: path: "{{ stacks_base_path }}/postgresql/.env" register: application_postgres_env_file changed_when: false when: not (application_postgres_production_env_file.stat.exists | default(false)) - name: "Fallback: Extract PostgreSQL password from legacy .env file" shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''" register: application_postgres_password changed_when: false failed_when: false when: - not (application_postgres_production_env_file.stat.exists | default(false)) - application_postgres_env_file.stat.exists no_log: yes - name: Determine application database password set_fact: application_db_password: >- {% if application_compose_suffix == 'staging' %} {{ (application_postgres_staging_env_file.stat.exists | default(false) and application_postgres_staging_password.stdout | default('') != '') | ternary(application_postgres_staging_password.stdout, (application_postgres_env_file.stat.exists | default(false) and application_postgres_password.stdout | default('') != '') | ternary(application_postgres_password.stdout, vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')))) }} {% else %} {{ (application_postgres_production_env_file.stat.exists | default(false) and application_postgres_production_password.stdout | default('') != '') | ternary(application_postgres_production_password.stdout, (application_postgres_env_file.stat.exists | default(false) and application_postgres_password.stdout | default('') != '') | ternary(application_postgres_password.stdout, vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')))) }} {% endif %} no_log: yes - name: Determine application redis password set_fact: application_redis_password: "{{ redis_password | default(vault_redis_password | default('')) }}" no_log: yes - name: Ensure redis password provided via vault fail: msg: >- Redis credentials are missing. Define vault_redis_password in {{ application_vault_file }} (encrypted with ansible-vault) or pass redis_password via extra vars. when: (application_redis_password | string | trim) == '' - name: Determine application app key set_fact: application_app_key: "{{ app_key | default(vault_app_key | default('')) }}" no_log: yes - name: Ensure application app key provided via vault fail: msg: >- Application key missing. Define vault_app_key in {{ application_vault_file }} (ansible-vault) or pass app_key via extra vars. when: (application_app_key | string | trim) == '' - name: Determine encryption key (optional) set_fact: application_encryption_key: "{{ encryption_key | default(vault_encryption_key | default('')) }}" no_log: yes - name: Determine project root directory set_fact: project_root: "{{ playbook_dir | default(role_path + '/..') | dirname | dirname | dirname }}" changed_when: false - name: Check if application docker-compose.base.yml source exists locally (in project root) stat: path: "{{ project_root }}/docker-compose.base.yml" delegate_to: localhost register: application_compose_base_src become: no - name: Check if application docker-compose override file exists locally (production or staging) stat: path: "{{ project_root }}/docker-compose.{{ application_compose_suffix }}" delegate_to: localhost register: application_compose_override_src become: no - name: Check if production-base.yml exists (preferred for production/staging) stat: path: "{{ project_root }}/docker-compose.production-base.yml" delegate_to: localhost register: application_compose_production_base_src become: no - name: Copy application docker-compose.production-base.yml to target host (production/staging) copy: src: "{{ project_root }}/docker-compose.production-base.yml" dest: "{{ application_stack_dest }}/docker-compose.base.yml" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0644' when: application_compose_production_base_src.stat.exists - name: Copy application docker-compose.base.yml to target host (fallback) copy: src: "{{ project_root }}/docker-compose.base.yml" dest: "{{ application_stack_dest }}/docker-compose.base.yml" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0644' when: - not application_compose_production_base_src.stat.exists - application_compose_base_src.stat.exists - name: Copy application docker-compose override file to target host (production or staging) copy: src: "{{ project_root }}/docker-compose.{{ application_compose_suffix }}" dest: "{{ application_stack_dest }}/docker-compose.{{ application_compose_suffix }}" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0644' when: application_compose_override_src.stat.exists - name: Check if legacy docker-compose.yml exists (fallback) stat: path: "{{ application_stack_src }}/docker-compose.yml" delegate_to: localhost register: application_compose_src become: no when: not (application_compose_base_src.stat.exists | default(false)) - name: Copy application docker-compose.yml to target host (fallback for legacy) copy: src: "{{ application_stack_src }}/docker-compose.yml" dest: "{{ application_stack_dest }}/docker-compose.yml" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0644' when: - application_compose_src is defined - application_compose_src.stat.exists | default(false) - not (application_compose_base_src.stat.exists | default(false)) - name: Check if nginx configuration exists locally stat: path: "{{ application_stack_src }}/nginx" delegate_to: localhost register: application_nginx_src become: no - name: Synchronize nginx configuration copy: src: "{{ application_stack_src }}/nginx/" dest: "{{ application_stack_dest }}/nginx/" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0644' when: application_nginx_src.stat.exists - name: Debug - Check available variables before set_fact debug: msg: - "application_environment: {{ application_environment | default('NOT SET') }}" - "app_env: {{ app_env | default('NOT SET') }}" - "application_compose_suffix: {{ application_compose_suffix | default('NOT SET') }}" - "app_domain (from vars): {{ 'DEFINED' if app_domain is defined else 'NOT SET' }}" - "db_user_default: {{ db_user_default | default('NOT SET') }}" - "db_name_default: {{ db_name_default | default('NOT SET') }}" - "db_host_default: {{ db_host_default | default('NOT SET') }}" - "application_db_password: {{ 'SET (length: ' + (application_db_password | default('') | string | length | string) + ')' if (application_db_password | default('') | string | trim) != '' else 'NOT SET' }}" - "application_redis_password: {{ 'SET (length: ' + (application_redis_password | default('') | string | length | string) + ')' if (application_redis_password | default('') | string | trim) != '' else 'NOT SET' }}" - "application_app_key: {{ 'SET (length: ' + (application_app_key | default('') | string | length | string) + ')' if (application_app_key | default('') | string | trim) != '' else 'NOT SET' }}" changed_when: false - name: Determine application environment for domain resolution set_fact: _app_env: "{{ app_env | default(application_environment | default('production')) }}" no_log: yes - name: Expose secrets for template rendering (step 1 - basic vars) set_fact: db_password: "{{ application_db_password | default('') }}" redis_password: "{{ application_redis_password | default('') }}" app_key: "{{ application_app_key | default('') }}" encryption_key: "{{ application_encryption_key | default('') }}" app_env: "{{ _app_env }}" minio_root_user: "{{ minio_root_user | default('minioadmin') }}" minio_root_password: "{{ minio_root_password | default('') }}" no_log: yes - name: Expose secrets for template rendering (step 2 - db vars) set_fact: db_username: "{{ db_user | default(db_user_default | default('postgres')) }}" db_name: "{{ db_name | default(db_name_default | default('michaelschiemer')) }}" no_log: yes - name: Expose secrets for template rendering (step 3 - db_host with conditional) set_fact: db_host: >- {%- if db_host is defined and db_host | string | trim != '' -%} {{ db_host }} {%- elif db_host_default is defined and db_host_default | string | trim != '' -%} {{ db_host_default }} {%- elif application_compose_suffix == 'production.yml' -%} postgres-production {%- elif application_compose_suffix == 'staging.yml' -%} postgres-staging {%- else -%} postgres {%- endif -%} no_log: yes - name: Expose secrets for template rendering (step 4 - app_domain) set_fact: app_domain: >- {%- if app_domain is defined and app_domain | string | trim != '' -%} {{ app_domain }} {%- elif _app_env == 'production' -%} michaelschiemer.de {%- else -%} staging.michaelschiemer.de {%- endif -%} no_log: yes - name: Render application environment file template: src: "{{ application_env_template }}" dest: "{{ application_stack_dest }}/.env" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0600' - name: Create Docker Compose secret files from determined passwords copy: content: "{{ item.value }}" dest: "{{ application_stack_dest }}/secrets/{{ item.name }}.txt" owner: "{{ ansible_user }}" group: "{{ ansible_user }}" mode: '0600' loop: - name: db_user_password value: "{{ application_db_password }}" - name: redis_password value: "{{ application_redis_password }}" - name: app_key value: "{{ application_app_key }}" - name: vault_encryption_key value: "{{ application_encryption_key | default(application_app_key) }}" no_log: yes