Write Multiline Variable Content into YML File in Ansible

ansible

The task: what works/what not

Replacing/adding one-liners is working as expected

A file sentry/config.yml has the following line(s)

# ...
# github-app.webhook-secret: 'GITHUB_WEBHOOK_SECRET' # Use only if configured in GitHub
# github-app.client-id: 'GITHUB_CLIENT_ID'
# github-app.client-secret: 'GITHUB_CLIENT_SECRET'
# github-app.private-key: |
#   -----BEGIN RSA PRIVATE KEY-----
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   -----END RSA PRIVATE KEY-----
# ...

which should be replaced by ansible variables.

However the one-liners could be replaced/added without problems with following ansible task (the vars are added directly for better overview):

- name: "Configure sentry misc settings."
  vars:
    sentry_config:
      auth-google.client-id: "{{ secret_sentry_googleauth_clientid }}"
      auth-google.client-secret: "{{ secret_sentry_googleauth_clientkey }}"
      github-app.id: "{{ secret_sentry_githubapp_id }}"
      github-app.name: "{{ secret_sentry_githubapp_name }}"
      github-app.client-id: "{{ secret_sentry_githubapp_clientid }}"
      github-app.client-secret: "{{ secret_sentry_githubapp_clientsecret }}"
      github-app.webhook-secret: "{{ secret_sentry_githubapp_webhooksecret }}"
      github-app.private-key: "{{ secret_sentry_githubapp_privatekey }}"
  loop: "{{ sentry_config | dict2items }}"
  ansible.builtin.lineinfile:
    path: "{{ sentry_docker_compose_project_folder }}/sentry/config.yml"
    regexp: "^#?\\s*{{ item.key }}:"
    line: "{{ item.key }}: {{ item.value | to_json }}"

The question: What is the best way to fill github-app.private-key with a multiline variable value?

replacing a resulting multiline yaml value with ansible needs another workaround

The given multiline string in sentry/config.yml

# github-app.private-key: |
#   -----BEGIN RSA PRIVATE KEY-----
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   -----END RSA PRIVATE KEY-----

must be

  • uncommented
  • and the value of the RSA private key must be added "as-is" with correct spacing on each line from the ansible var that the replacement results in
github-app.private-key: |
  -----BEGIN RSA PRIVATE KEY-----
  real privatekey from ansible var privatekey from ansible var
  real privatekey from ansible var privatekey from ansible var
  real privatekey from ansible var privatekey from ansible var
  real privatekey from ansible var privatekey from ansible var
  -----END RSA PRIVATE KEY-----

Best Answer

The question: What is the best way ...

The answer should be always: reduce complexity, keep it as simple as possible, remove as much up to the point when there is nothing left to take away, use divide-and-conquer algorithm.

One approach would be simply pre-process the given config file

sentry/config.yml

# start: here
# github-app.webhook-secret: 'GITHUB_WEBHOOK_SECRET'
# github-app.client-id: 'GITHUB_CLIENT_ID'
# github-app.client-secret: 'GITHUB_CLIENT_SECRET'
# github-app.private-key: |
#   -----BEGIN RSA PRIVATE KEY-----
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   privatekeyprivatekeyprivatekeyprivatekey
#   -----END RSA PRIVATE KEY-----
# end: here

because of Complex data manipulation

... while Ansible is not recommended as a data processing/manipulation tool ...

A minimal example playbook

---
- hosts: localhost
  become: false
  gather_facts: false

  vars:

    config_file: "config.yml"

  pre_tasks: # data cleansing

  - name: Create temporary uncommented version
    shell:
      cmd: "sed 's/# //g' sentry/{{ config_file }} > /tmp/{{ config_file }}"

  tasks:

  - name: Load variables from pre-processed temporary file
    include_vars:
      file: "/tmp/{{ config_file }}"
      name: secret_sentry

  - name: Show variables
    debug:
      msg: "{{ secret_sentry }}"

will result into an output of

TASK [Show variables] ****************************
ok: [localhost] =>
  msg:
    end: here
    github-app.client-id: GITHUB_CLIENT_ID
    github-app.client-secret: GITHUB_CLIENT_SECRET
    github-app.private-key: |-
      -----BEGIN RSA PRIVATE KEY-----
      privatekeyprivatekeyprivatekeyprivatekey
      privatekeyprivatekeyprivatekeyprivatekey
      privatekeyprivatekeyprivatekeyprivatekey
      privatekeyprivatekeyprivatekeyprivatekey
      privatekeyprivatekeyprivatekeyprivatekey
      -----END RSA PRIVATE KEY-----
    github-app.webhook-secret: GITHUB_WEBHOOK_SECRET
    start: here