Deploying to AWS with Ansible and Terraform

Terraform is a powerful provisioning tool that supports immutable infrastructure and a declarative language

Süha Sülün
Automation Avengers
9 min readSep 11, 2018

--

I am sure about the fact that by now, most of us have used public cloud services like AWS, Azure, & Google Cloud. Well, if not all three, at least aws for sure(because aws is the biggest player in the public cloud service offering). Creating your infrastructure on top of these public cloud is pretty straightforward and easy, if done manually, by using their respective web console. However, it is not that simple to automate the infrastructure building process in a reusable fashion. Please keep the fact in mind that we are talking about automating the “infrastructure” here and not about your applications and services running on your servers.

When I say infrastructure, I am referring to the below in the cloud..

  • Networks
  • Subnetworks
  • Firewalls
  • LoadBalancers
  • Storage
  • Public IPs
  • DNS Entries.. And much more..

There are already many configuration management tools out there in the market, that can automate your Applications and Services running inside instances(VMs). For example, Puppet, Chef, Ansible, Salt etc can be used for automating your applications and services running inside your VM (or in other words…your app running in the infrastructure).

We need a method and a reusable process to build infrastructure using code. The idea is to basically treat the infrastructure components that we listed above, in the same manner that we treat our application(ie: Using code). Hence the name “Infrastructure As code”. The tool that we are going to discuss today falls under IaC (Infra as Code). It is called “Terraform”

So basically the principles that we generally apply to software development can then be applied to infrastructure as well. Like version controlling, Infrastructure can be shared(because its code), can go back in time (because we can go to the previous version).

You can declare the required state of your infrastructure using Terraform, and it will take care of the underlying complexities to create it.

Let’s imagine you want to create an AWS instance, and then attach a public IP(elastic ip), and then finally add a DNS entry for your instance. As I mentioned earlier, you simply specify the end state that you want using terraform.

  • A Public IP
  • A DNS Entry
  • An Instance

There are dependencies between each step that we have above. Terraform will calculate the dependencies and create each of the resources above in the correct order. Let’s think about this for a moment. An instance should be created before we can attach a public IP to it. The public IP should be created before adding the DNS entry. Which means the order is important here, and terraform will take care of this by building a graph internally.

So basically terraform will provision your infra in a cloud of your interest (terraform also falls under the umbrella of tools called as provisioners). Hence it can be also be called a cloud provisioner.

Why Can’t We Use Puppet, Chef or Ansible for this?

The primary area where Puppet, Chef and Ansible focuses is on configuration inside the instances (ie: Your application and server specific configs). Although there are modules available to use these configuration management tools to manage some of the infrastructure stuff, the original intent behind their creation was application configuration inside the operating system.

Being said that, you can still use these configuration management tools along with terraform to configure things inside the VMs as we will use in this tutorial(basically these tools can be used by terraform as a provisioner to configure applications inside your infrastructure).

Apart from this, if you are using Docker containers for running your applications, the containers are self sufficient and will have all the required configuration packed into it for your application. In this case, the need of a configuration management tool like chef or puppet is not that much. But you still need something to manage your infrastructure with code. Because the containers will ultimately run on top of a server/vm in a cloud infra. Terraform can step in and create your required infra for your containers to run on.

Lets not deny the fact that all these tools(chef, puppet, ansible etc) can be used as IaC(Infrastructure as Code) as well. But terraform is well suited for this purpose as it maintains the state of the infrastructure.

Tutorial focus

  1. Jenkins will trigger tests/deployment/provisioning as CI/CD
  2. **I suggest to add test steps via Gerrit Pull request which runs terraform validate/plan steps before apply commands
  3. Packer will create our production grade baseimage OS and save AWS AMI
  4. Terraform will provision AWS VPC and deploy infra IAC
  5. Ansible will deploy/test application on EC2 instance
https://www.terraform.io/downloads.html

Pull and run latest Jenkins Blueocean docker image:

Jenkins Configuration

  1. Install suggested plugins
  2. Add awsCred, repoCred credentials for AWS Account and Github account.
  3. AWS user must have EC2FullAccess rights.
  4. Create new pipeline and use https://github.com/repository repo as Jenkinsfile source.

Required:

yourdockerhub/agent-image for docker agent (git, bash, jdk, packer, terraform preinstalled) or build your own image with below dockerfile and push to registry.

Dockerfile for jenkins agent

Jenkins job pulls docker image and runs commands in it.

Pipeline has 3 stages for create AWS Environment;

Checkout SCM repository

Jenkinsfile

Create EC2 AMI in AWS with Packer and create ELB, ASG, LC, SG, AZ with Terraform and commit AWS Environment state to repository repo.

Jenkins Blueocean Pipeline

Packer; (awsCred should be set in Jenkins Credentials properly)

Provisions Unix machine from Europe Region and perform below task on AMI;

· Install suitable docker version for OS

· Install Ansible

· Download SpringBoot artifact

· Build docker image and tag as springboot/app:latest

· Run latest springboot/app latest image on machine

· Run ansible-playbook (install git, nginx) Test Docker image is running

· Save AMI in AWS as name like prod-image*

Packer.json

Terraform; (awsCred should be set in Jenkins Credentials properly)

Create AWS Resources and Deployment

· Provisions latest prod-image* AMI

· Apply Launch Configuration settings with given Security Group port settings

· Apply Auto Scale Group definitions min:2 to max:5 instances

· AMIs’ attach behind Elastic Load Balancer

· Spin cross in All Availibility Zones

· Commits Env state to https://github.com/suhasulun/repository repo

· Deployment is done

· Lifecycle hook is set as create before destroy enabled

*You may choose to use .tfvars file to read variables from local and ofc not in version control system or just use vault and invoke for variable&credential reading.

Terraformfile

Check your infra running from ELB DNS_Name A Record as loadbalanced

Springboot App is running on ELB

--

--