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.