I wanted to add my AWS access keys to my instance without manually copying them over. I’m using Ansible to manually provision a Terraform instance, so it’ll need the keys to interact with AWS services.

Of course, you can’t just add the keys to your repo. Anyone with access to the repo can see your keys, which is especially bad if your repo is semi or fully public.

The solution is ansible-vault. ansible-vault makes it easy to encrypt and decrypt files and variables.

Here’s how I used it:

I wanted to do be able to call this in my playbook:

---
- name: Give AWS auth access
  hosts: aws_auth
  remote_user: ubuntu
  become: yes
  vars:
    env_var_file: /home/ubuntu/.bashrc

  tasks:
    - name: Setup AWS Authentication
      block:
        - name: Add authentication keys to .bashrc
          ansible.builtin.lineinfile:
            path: "{{ env_var_file }}"
            line: "{{ item }}"
            state: present
          loop:
            - "export AWS_ACCESS_KEY_ID={{ aws_access_key_id }}"
            - "export AWS_SECRET_ACCESS_KEY={{ aws_secret_access_key }}"

Of course, I don’t want to expose my access keys!

The path, env_var_file, is just the user’s .bashrc. I’m setting these environment variables here so that I can log in and do AWS things through Terraform.

First, I created a group in my Ansible hosts file called [aws_auth]. I added my instance to that group.

Then I set up a password file:

 echo 'somepassword' > ~/.ansible_vault_aws_auth_password

If you add a space before a command, that command won’t be added to your bash history, which is great for passwords.

I used Ansible to create an encrypted file using a password file. You can also have it prompt you for a password.

ansible-vault create group_vars/aws_auth.yml --vault-id aws_auth@~/.ansible_vault_aws_auth_password

The above command opens a vi editor, where you can add your secrets and save.

One notable thing here in my hosts file:

My Terraform server is in the [terraform] group. I created another group [aws_auth] only for servers that I’d like to have AWS authentication access. (I undid this when I converted to using a role in the below section, but I’ll likely add the group back in so I can keep track of servers that have AWS keys.)

Once you’ve done the above, you can execute the playbook like this:

ansible-playbook my-playbook.yml --vault-password-file ~/.ansible_vault_aws_auth_password

It’s easy with ansible-vault to encrypt, decrypt, edit, or re-key your secrets.

Converting to an Ansible Role

I’ve been keeping things pretty simple and running all my tasks in playbooks, but I figured this was a good time to create a role.

Using playbooks was starting to get a little messy, so I reorganized:

  • I created an aws_auth Ansible role.
  • I got rid of the aws_auth group.
  • I added the role to my Terraform playbook.

Here’s how I did it:

First, I created the directories:

mkdir -p roles/aws_auth/tasks
touch roles/aws_auth/tasks/main.yml
mkdir vars

I moved the encrypted aws_auth.yml to vars (and renamed it aws_auth_vars.yml), then I moved my tasks into the main.yml file that I touched above.

I got rid of the env_var_file variable that I used above to just call the value directly - I’ll probably never need to change it!

./roles/aws_auth/tasks/main.yml

---
- name: Setup AWS Authentication
  block:
    - name: Add authentication keys to .bashrc
      ansible.builtin.lineinfile:
        path: /home/ubuntu/.bashrc
        line: "{{ item }}"
        state: present
      loop:
        - "export AWS_ACCESS_KEY_ID={{ aws_access_key_id }}"
        - "export AWS_SECRET_ACCESS_KEY={{ aws_secret_access_key }}"

I’m using this role for my Terraform instance, so I’m calling it like this:

---
- name: Install Terraform
  hosts: terraform
  remote_user: ubuntu
  become: yes
  roles:
    - aws_auth
  vars_files:
    - ./vars/aws_auth_vars.yml

  tasks:
  ....

Note that I’m calling the additional aws_auth_vars.yml file here, which has the encrypted variables.

Why didn’t I stick these variables in the aws_auth role?

I don’t like nesting these types of files deeply, especially if I’m going to be rotating the contents.

Shorter Command

Adding the --vault-password-file <file> option might become onerous. You can omit it by adding the environment variable ANSIBLE_VAULT_PASSWORD_FILE like this:

export ANSIBLE_VAULT_PASSWORD_FILE=<file>

Now I’m back to doing this:

ansible-playbook my-playbook.yml

This makes things a little simpler and quicker for testing!

How Can This Be Improved?

One thing I don’t like about this setup is that I can’t keep track of what server has AWS authorization keys!

Sure, I can remember a server or two, but I’d rather not have to remember anything.

I think the solution is to add AWS auth instances to a group in the hosts file, then add and remove them as required.