Simplified Retrieval of Terraform Output for Ansible via AWS SSM Parameter Store 🚀
Tags: aws, cloud, ansible, terraform
Published: 2023-11-07 13:24:17

I recently had to work with Ansible for the first time. I needed to install software on several EC2 instances, and I was given an Ansible playbook that I needed to execute.

When examining the structure of the Ansible folder, I noticed that I had to update certain variables within a file named 'specifics.yml.' These variables were formatted as follows: 

```yaml
nfs_mounts:
  - src: "myNfsDNSname:/"

my_super_infra:
  hosts:
    10.10.10.10:
```
I initially began working with it. However, as I continued testing, I found myself making numerous deployments with Terraform. Each time, I had to modify my code to include the new EFS DNS name, which proved to be quite a hassle.

For context, I had established a CI/CD pipeline using Gitlab-CI for this project, which had the following structure:


![Gitlab CI structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t4yqqprhy93oxfvyxmkl.png)

From that point on, I needed to discover a way to dynamically obtain values generated during the Terraform 'apply' stage. Initially, I considered using Terraform to create a file at the end of the 'apply' stage containing the output of my modules. While this approach would work, it would also make the execution of the Ansible job dependent on the completion of the Terraform 'apply' stage. This was not what I wanted; I aimed to run the Ansible playbook without having to go through Terraform each time.

So, I came up with this idea: introduce a Terraform module that stores the necessary variables in the AWS SSM Parameter Store. This module takes certain variables as input, which are the outputs of my modules that I wish to retrieve in Ansible, such as the EFS DNS name. To achieve this, you only need to include the following code snippet for each of your variables: 

```terraform
resource "aws_ssm_parameter" "efs_dns_url" {
  name  = "OUTPUT_EFS_DNS_URL"
  type  = "String"
  value = var.efs_dns_url
}
```

You can also use count if you have a list (for example a list of IPs) : 

```terraform
resource "aws_ssm_parameter" "my_ip" {
  count = length(var.my_ip)
  name  = "OUTPUT_MY_IP_${count.index}"
  type  = "String"
  value = var.my_ip[count.index]
}
```

With this step, the initial part of my strategy was set up. The next challenge was to retrieve these values within my Ansible 'specifics.yml' file. As I delved into the documentation, I came across [a page](https://docs.ansible.com/ansible/latest/collections/amazon/aws/ssm_parameter_lookup.html#ansible-collections-amazon-aws-ssm-parameter-lookup) in the Ansible documentation that was precisely what I needed! The first step was to include the requirements in the 'requirements.yml' file:

```yaml
  - name: amazon.aws
    version: 6.5.0
```

Then, you remember the specifics.yml ? it now looks like this : 

```yaml
nfs_mounts:
    - src: "{{ lookup('amazon.aws.aws_ssm', 'OUTPUT_EFS_DNS_URL', region='eu-central-1' ) }}:/"
```

It's as straightforward as that! You can now fetch specific values from Terraform directly into your Ansible playbook. This is incredibly convenient. The only drawback I've encountered here is the output when you execute the playbook, particularly when you utilize it for the host section, like:

```yaml
my_block:
  hosts:
    "{{ lookup('amazon.aws.aws_ssm', 'OUTPUT_MY_IP_0', region='eu-central-1' )}}":
    "{{ lookup('amazon.aws.aws_ssm', 'OUTPUT_MY_IP_1', region='eu-central-1' )}}":
```

When you run the playbook with this kind of setup, you get an output which looks like this :

![Ansible host ugly output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/35n1gtk2idxymn6yke6u.png)

This is not ideal, you can change it using variables, but I haven’t found a way yet to get the IP here instead of a placeholder. If you have an idea, feel free to reach out to me via the comments or on my socials.