Terraform is a great tool for building infrastructure in the cloud. Ansible is a beautifully simple agentless (and serverless) configuration management tool. A common use case is to build servers with Terraform, and have Ansible configure them. Unfortunately Terraform lacks a provisioning plugin for Ansible - but fear not, they can be used together fairly trivially by using the local-exec provisioner of Terraform.

Let’s take an example of creating a Jenkins master server in AWS EC2. Here is the Terraform specification for the instance:

resource "aws_instance" "jenkins_master" {
    # Use an Ubuntu image in eu-west-1
    ami = "ami-f95ef58a"

    instance_type = "t2.small"

    tags {
        Name = "jenkins-master"
    }

    # We're assuming the subnet and security group have been defined earlier on

    subnet_id = "${aws_subnet.jenkins.id}"
    security_group_ids = ["${aws_security_group.jenkins_master.id}"]
    associate_public_ip_address = true

    # We're assuming there's a key with this name already
    key_name = "deployer-key"

    # This is where we configure the instance with ansible-playbook
    provisioner "local-exec" {
        command = "sleep 120; ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -u ubuntu --private-key ./deployer.pem -i '${aws_instance.jenkins_master.public_ip},' master.yml"
    }
}

To break down the local-exec command, we’re first sleeping 120 seconds just to give the instance some time to boot up before having Ansible knock on the door. After that we’re setting the ANSIBLE_HOST_KEY_CHECKING environment variable to False, to make Ansible trust the newly-launched server. Then we are running the ansible-playbook command, with the user ubuntu, a specified private key, and most importantly: a specified Ansible inventory that only contains the public IP of the server we’ve just launched.

Here is an accompanying Ansible playbook (master.yml) for installing Jenkins:

- hosts: all
  become: true

  tasks:
    - name: ensure the jenkins apt repository key is installed
      apt_key: url=https://pkg.jenkins.io/debian-stable/jenkins.io.key state=present

    - name: ensure the repository is configured
      apt_repository: repo='deb https://pkg.jenkins.io/debian-stable binary/' state=present

    - name: ensure jenkins is installed
      apt: name=jenkins update_cache=yes

    - name: ensure jenkins is running
      service: name=jenkins state=started

With both of these in a directory, you will be able to provision a server with Terraform and have Ansible configure it automatically.

While we use Jenkins as an example here, one thing to consider would be persistent storage for the Jenkins installation. One approach is to create an EFS volume with Terraform, and store the Jenkins home directory on that volume. You can pass the EFS volume id to the Ansible playbook with -e 'efs_id=${aws_efs_file_system.volumename.id}'.

To mount an EFS volume with Ansible, see https://github.com/dmitrybelyakov/ansible-localcloud-efs


Releaseworks Academy has a free online training course on Docker & Jenkins best practices: https://www.releaseworksacademy.com/courses/best-practices-docker-jenkins