Skip to content
Welcome To Charanjit Cheema Blog

Welcome To Charanjit Cheema Blog

An Open Source and Cloud Blog

Menu
  • Home
  • About Me!
  • Way to my Technical Blog
  • Contact me
  • Privacy Policy
Menu

Terraform and Ansible Collaboration for AWS Cloud Deployment

Posted on August 28, 2024August 28, 2024 by Charanjit Singh

In modern IT, automation and efficiency are crucial. Terraform and Ansible are leading Infrastructure as Code (IaC) tools, each with unique strengths. Terraform excels at provisioning cloud infrastructure across multiple providers using a declarative language, while Ansible focuses on configuration management and application deployment through agentless automation.

 

Terraform Overview

Terraform, developed by HashiCorp, allows you to define and manage cloud resources with code. It supports various cloud providers, making it ideal for multi-cloud strategies. Key features include:

  • IaC and Multi-cloud Support: Define infrastructure with code and manage it across different cloud environments.
  • Provisioning: Automate the creation and management of infrastructure components.

 

Ansible Overview

Ansible, by Red Hat, automates configuration management, application deployment, and orchestration. It uses YAML-based playbooks and does not require any agents, simplifying setup and management. Key features include:

  • Configuration Management: Ensures consistency across systems by automating their configuration.
  • Agentless Operation: No need for agents on managed systems, reducing overhead.

 

Benefits of Integrating Terraform and Ansible

Combining Terraform and Ansible leverages their respective strengths:

  1. Complete Automation: Terraform handles infrastructure provisioning, while Ansible manages configuration, allowing for full automation from setup to deployment.
  2. Efficiency and Consistency: Automate both provisioning and configuration to reduce errors and ensure consistency across environments.
  3. Scalability and Flexibility: Manage multi-cloud and hybrid environments efficiently by using Terraform for infrastructure and Ansible for uniform configuration.
  4. Enhanced Collaboration: Use version-controlled code for infrastructure and configuration, enabling better team collaboration and transparency.
  5. Simplified Management: Automate complex setups with Terraform and use Ansible for ongoing configuration tasks, ensuring seamless operation.
  6. Security and Compliance: Terraform provisions secure resources while Ansible enforces security policies, enhancing overall compliance.

 

Prerequisites for Setting Up Terraform and Ansible Integration

  • AWS Account: You need an active AWS account to provision resources on AWS.
  • AWS Access and Secret keys: If you do not have these keys, you can create them by following this AWS IAM guide
  • Terraform Installed: Ensure Terraform is installed on your local machine. You can download it from the Terraform website.
  • Ansible Installed: Install Ansible on your local machine. Instructions can be found in the Ansible documentation.
  • SSH Key Pair: Generate an SSH key pair through AWS Console if you don’t have one, as you’ll need it to access your AWS EC2 instance. Ensure you have the private key file locally. Learn more about how to generate an SSH key pair here.
  • Basic Understanding of Terraform and Ansible: Familiarity with basic Terraform and Ansible concepts will help you follow along with the setup and configuration processes.
  • Text Editor: Use a text editor or IDE to create and modify Terraform and Ansible files. Popular choices include VS Code, Sublime Text, or Atom or if you are using Linux machine then you can choose either vi editor or nano.

 

Deploying Infrastructure on AWS with Terraform and Ansible

Let’s explore how to use Terraform and Ansible together to deploy and configure infrastructure on AWS. Terraform provisions the infrastructure as code, and Ansible automates the configuration and deployment on the created resources. In this article, I’ll demonstrate how to provision an Amazon Linux instance on AWS using Terraform. Afterward, I’ll use the remote-exec provisioner to run an Ansible playbook that installs the latest patches, sets up the Apache HTTP server, and configures the web service.

  1. Directory Structure and Files

My project directory, terraform_ansible_collaboration, contains the following files:

.

├── ansible.cfg

├── main.tf

├── patch_apache_install.yml

├── README.md

├── terraform.tfstate

├── terraform.tfstate.backup

└── terraform.tfvars

 

Each file plays a specific role in the deployment process:

–  ansible.cfg: Ansible configuration file that defines default settings for Ansible, such as the remote user and host key checking behavior.

– main.tf: The main Terraform configuration file where AWS infrastructure is defined and provisioned.

– patch_apache_install.yml: An Ansible playbook that performs server configuration tasks like installing Apache, patching the system, and setting up a simple webpage.

– terraform.tfstate and terraform.tfstate.backup: Terraform state files that track the state of the infrastructure.

– terraform.tfvars: A variables file used by Terraform to input sensitive data such as AWS credentials and SSH key paths.

– README.md: A file that typically contains documentation or instructions for using the project.

 

  1. Terraform Configuration (main.tf)

The main.tf file is the heart of my infrastructure deployment, defining the resources needed to launch an AWS EC2 instance and the security group configurations required for my web server.

Here’s a breakdown of its contents:

 

terraform {

required_providers {

aws = {

source  = “hashicorp/aws”

version = “~> 5.0”

}

}

}

 

provider “aws” {

region     = var.aws_region

access_key = var.aws_access_key

secret_key = var.aws_secret_key

}

 

 

–  terraform block: Specifies the required providers and their versions. Here, I am using the AWS provider version ~> 5.0.

– provider block: Defines AWS as the cloud provider and sets up access using variables for the AWS region, access key, and secret key.

 

resource “aws_instance” “web_server” {

ami           = “ami-02b49a24cfb95941c” # Replace with your preferred AMI ID

instance_type = “t2.micro”

key_name      = var.key_pair_name

 

vpc_security_group_ids = [aws_security_group.web_sg.id]

 

tags = {

Name = “WebServer”

}

 

provisioner “remote-exec” {

inline = [

“echo ‘Hello World! I am cj-web01 and I am alive.'”

]

 

connection {

type        = “ssh”

user        = “ec2-user”

private_key = file(var.private_key_path)

host        = self.public_ip

}

}

 

provisioner “local-exec” {

command = <<EOT

ansible-playbook -i ${self.public_ip}, –private-key ${var.private_key_path} patch_apache_install.yml

EOT

}

}

 

 

–  aws_instance resource: Creates an EC2 instance using a specified AMI ID and instance type (t2.micro). It also specifies an SSH key for access and associates the instance with a security group.

–  remote-exec provisioner: Executes a simple shell command to ensure the EC2 instance is accessible via SSH right after provisioning.

– local-exec provisioner: Runs an Ansible playbook (patch_apache_install.yml) after the EC2 instance is up and running, using its public IP.

 

resource “aws_security_group” “web_sg” {

name        = “web_sg”

description = “Allow HTTP and SSH access”

 

ingress {

from_port   = 22

to_port     = 22

protocol    = “tcp”

cidr_blocks = [“0.0.0.0/0”]

}

 

ingress {

from_port   = 80

to_port     = 80

protocol    = “tcp”

cidr_blocks = [“0.0.0.0/0”]

}

 

egress {

from_port   = 0

to_port     = 0

protocol    = “-1”

cidr_blocks = [“0.0.0.0/0”]

}

}

 

 

– aws_security_group resource: Defines a security group allowing inbound SSH (port 22) and HTTP (port 80) traffic from any IP and outbound traffic to any IP.

 

  1. Ansible Playbook (patch_apache_install.yml)

The Ansible playbook patch_apache_install.yml is executed after Terraform provisions the EC2 instance. It performs several tasks to configure the web server:

 

– hosts: all

become: yes

tasks:

– name: Set the Server Hostname

hostname:

name: cj-web01

 

– name: Install patches

dnf:

name: “*”

state: latest

 

– name: Install Apache

dnf:

name: httpd

state: present

 

– name: Start Apache service

service:

name: httpd

state: started

enabled: yes

 

– name: Configure webpage

copy:

content: “Welcome to \”{{ ansible_hostname }}\””

dest: /var/www/html/index.html

 

–  Set the Server Hostname: Sets the hostname of the instance to cj-web01.

–  Install patches: Uses of dnf ansible module to update all packages to the latest version.

–  Install Apache: Installs the Apache HTTP server.

–  Start Apache service: Ensures the Apache service is started and enabled to run on boot.

–  Configure webpage: Copies a simple HTML file to the web server’s root directory, displaying a welcome message that includes the server’s hostname.

 

  1. Terraform Variables (terraform.tfvars)

The terraform.tfvars file provides the sensitive data and configurations required by Terraform to provision resources:

 

aws_access_key   = “DCBA8ZABCDEFD0ABC6”

aws_secret_key   = “GOABGlv90iooa9REDFmbP9qPA900c1zsYHS5n6QJ”

key_pair_name    = “testkeypair”

private_key_path = “/home/foobar/key/testkeypair.pem”

 

– aws_access_key and aws_secret_key: Credentials for AWS access.

– key_pair_name: Name of the SSH key pair used for accessing the EC2 instance.

– private_key_path: Local path to the private key file for SSH access.

 

  1. Ansible Configuration (ansible.cfg)

Since I’m using a different login ID to access my Linux machine for deploying and configuring AWS EC2 instances with Terraform and Ansible, I’ve configured the ansible.cfg file to specify ec2-user as the default remote user for running playbooks. Additionally, I’ve disabled SSH host key checking to prevent Ansible from getting stuck due to mismatched host keys.

Here’s how the ansible.cfg file is configured:

 

[defaults]

remote_user = ec2-user

host_key_checking = False

 

 

Deployment Process

  1. Provisioning Infrastructure with Terraform: Run terraform init to initialize the project and terraform apply to provision the resources. Terraform uses the configurations defined in main.tf to create an EC2 instance and a security group.

 

  1. Configuring Server with Ansible: Once the EC2 instance is created, Terraform’s local-exec provisioner triggers the Ansible playbook patch_apache_install.yml, which configures the server by updating packages, installing Apache, and setting up a basic webpage. Below is the output of terraform apply command:

charanjit@charanjit-ubuntu:~/terraform_ansible_collaboration$ terraform apply –auto-approve

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create

Terraform will perform the following actions:

# aws_instance.web_server will be created
+ resource “aws_instance” “web_server” {
+ ami = “ami-02b49a24cfb95941c”
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = “t2.micro”
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = “testkeypair”
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ “Name” = “WebServer”
}
+ tags_all = {
+ “Name” = “WebServer”
}
+ tenancy = (known after apply)
+ user_data = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
}

# aws_security_group.web_sg will be created
+ resource “aws_security_group” “web_sg” {
+ arn = (known after apply)
+ description = “Allow HTTP and SSH access”
+ egress = [
+ {
+ cidr_blocks = [
+ “0.0.0.0/0”,
]
+ from_port = 0
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = “-1”
+ security_groups = []
+ self = false
+ to_port = 0
# (1 unchanged attribute hidden)
},
]
+ id = (known after apply)
+ ingress = [
+ {
+ cidr_blocks = [
+ “0.0.0.0/0”,
]
+ from_port = 22
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = “tcp”
+ security_groups = []
+ self = false
+ to_port = 22
# (1 unchanged attribute hidden)
},
+ {
+ cidr_blocks = [
+ “0.0.0.0/0”,
]
+ from_port = 80
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = “tcp”
+ security_groups = []
+ self = false
+ to_port = 80
# (1 unchanged attribute hidden)
},
]
+ name = “web_sg”
+ name_prefix = (known after apply)
+ owner_id = (known after apply)
+ revoke_rules_on_delete = false
+ tags_all = (known after apply)
+ vpc_id = (known after apply)
}

Plan: 2 to add, 0 to change, 0 to destroy.
aws_security_group.web_sg: Creating…
aws_security_group.web_sg: Creation complete after 2s [id=sg-0e50aa60f433f26be]
aws_instance.web_server: Creating…
aws_instance.web_server: Still creating… [10s elapsed]
aws_instance.web_server: Still creating… [20s elapsed]
aws_instance.web_server: Still creating… [30s elapsed]
aws_instance.web_server: Provisioning with ‘remote-exec’…
aws_instance.web_server (remote-exec): Connecting to remote host via SSH…
aws_instance.web_server (remote-exec): Host: 13.126.205.105
aws_instance.web_server (remote-exec): User: ec2-user
aws_instance.web_server (remote-exec): Password: false
aws_instance.web_server (remote-exec): Private key: true
aws_instance.web_server (remote-exec): Certificate: false
aws_instance.web_server (remote-exec): SSH Agent: false
aws_instance.web_server (remote-exec): Checking Host Key: false
aws_instance.web_server (remote-exec): Target Platform: unix
aws_instance.web_server (remote-exec): Connecting to remote host via SSH…
aws_instance.web_server (remote-exec): Host: 13.126.205.105
aws_instance.web_server (remote-exec): User: ec2-user
aws_instance.web_server (remote-exec): Password: false
aws_instance.web_server (remote-exec): Private key: true
aws_instance.web_server (remote-exec): Certificate: false
aws_instance.web_server (remote-exec): SSH Agent: false
aws_instance.web_server (remote-exec): Checking Host Key: false
aws_instance.web_server (remote-exec): Target Platform: unix
aws_instance.web_server (remote-exec): Connected!
aws_instance.web_server (remote-exec): Hello World! I am cj-web01 and I am alive.
aws_instance.web_server: Provisioning with ‘local-exec’…
aws_instance.web_server (local-exec): Executing: [“/bin/sh” “-c” ” ansible-playbook -i 13.126.205.105, –private-key /home/foobar/key/testkeypair.pem patch_apache_install.yml\n”]

aws_instance.web_server (local-exec): PLAY [all] *********************************************************************

aws_instance.web_server (local-exec): TASK [Gathering Facts] *********************************************************
aws_instance.web_server (local-exec): [WARNING]: Platform linux on host 13.126.205.105 is using the discovered Python
aws_instance.web_server (local-exec): interpreter at /usr/bin/python3.9, but future installation of another Python
aws_instance.web_server (local-exec): interpreter could change the meaning of that path. See https://docs.ansible.com
aws_instance.web_server (local-exec): /ansible/2.10/reference_appendices/interpreter_discovery.html for more
aws_instance.web_server (local-exec): information.
aws_instance.web_server (local-exec): ok: [13.126.205.105]

aws_instance.web_server (local-exec): TASK [Set the Server Hostname] *************************************************
aws_instance.web_server (local-exec): changed: [13.126.205.105]

aws_instance.web_server (local-exec): TASK [Install patches] *********************************************************
aws_instance.web_server: Still creating… [40s elapsed]
aws_instance.web_server: Still creating… [50s elapsed]
aws_instance.web_server (local-exec): ok: [13.126.205.105]

aws_instance.web_server (local-exec): TASK [Install Apache] **********************************************************
aws_instance.web_server: Still creating… [1m0s elapsed]
aws_instance.web_server (local-exec): changed: [13.126.205.105]

aws_instance.web_server (local-exec): TASK [Start Apache service] ****************************************************
aws_instance.web_server (local-exec): changed: [13.126.205.105]

aws_instance.web_server (local-exec): TASK [Configure webpage] *******************************************************
aws_instance.web_server (local-exec): changed: [13.126.205.105]

aws_instance.web_server (local-exec): PLAY RECAP *********************************************************************
aws_instance.web_server (local-exec): 13.126.205.105 : ok=6 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

aws_instance.web_server: Creation complete after 1m5s [id=i-011713f9707417fd1]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
charanjit@charanjit-ubuntu:~/terraform_ansible_collaboration$

  1. Verification: After deployment, you can verify the configuration by accessing the EC2 instance via SSH and checking the Apache service status or by visiting the public IP of the instance in a web browser to see the welcome page.:

Screenshot shows provisioned AWS EC2 instance status in AWS Console

Screenshot of installed Apache verification

 

Website working status screenshot

Note:

You can get this Terraform and Ansible code from my below GitHub link:
https://github.com/cjcheema/terraform_ansible_collaboration

You can also use an Ansible playbook to invoke a Terraform module and perform the same tasks. Learn more about this module at the link below:

https://docs.ansible.com/ansible/latest/collections/community/general/terraform_module.html

 

Wrap up!

By combining Terraform for infrastructure provisioning and Ansible for configuration management, you can automate the entire lifecycle of your cloud resources, from creation to configuration. This integration streamlines the deployment process, reduces manual effort, and ensures consistency across environments. By following the steps and using the configurations provided in this article and my GitHub link, you can set up a fully automated AWS deployment workflow with Terraform and Ansible.

 

Loading

  • Author
  • Recent Posts
Charanjit Singh
Follow him
Charanjit Singh
Charanjit is currently working as a Cloud Architect at Mphasis, with 18 years of experience in IT infrastructure projects, implementation, and support. While his main role is as a DevOps engineer, he holds a Cloud Architect position and has strong skills in cloud technologies and automation. His expertise includes Terraform, AWS, Azure DevOps, Azure Cloud, VMware, and Linux systems.

Charanjit is passionate about automating tasks and improving processes. He uses tools like Terraform and Azure DevOps to build and manage cloud infrastructure and streamline deployment. He also enjoys using Shell scripts and Ansible playbooks to make systems run more efficiently.

In his free time, Charanjit enjoys learning about new technologies and sharing his knowledge through his blog. When he’s not working, he likes listening to music, having a cup of coffee, and relaxing in nature.

You can connect with Charanjit on Twitter, Facebook, LinkedIn, or email him at charanjit.cheema@cjcheema.com.
Charanjit Singh
Follow him
Latest posts by Charanjit Singh (see all)
  • How to Deploy Docker Containers with NGINX on AWS EC2 Using Ansible and GitHub Actions - April 26, 2025
  • No More DynamoDB! Use Native S3 locking for Terraform State - February 7, 2025
  • How to Bring and Manage Manually Created AWS Resources Under Terraform Management - January 31, 2025

Like this:

Like Loading...

Related

2 thoughts on “Terraform and Ansible Collaboration for AWS Cloud Deployment”

  1. christinatodd2020aeaa798563 says:
    December 27, 2024 at 2:07 pm

    You gave some most informative knowledge, i have soo much interest on coding and this is so helpful to me to learn further, thank you for sharing this

    Loading...
    Reply
    1. Charanjit Singh says:
      January 26, 2025 at 8:36 pm

      I am happy you liked my article, please share it in your network.

      Loading...
      Reply

Leave a ReplyCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Tags

AWS Cloud Computing Dockers Networking Open Networking OpenSource RHEL-CentOS SDN Server Hardware SLES tcpdump Ubuntu WSL

Follow me @

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 2 other subscribers

Recent Posts

  • How to Deploy Docker Containers with NGINX on AWS EC2 Using Ansible and GitHub Actions
  • No More DynamoDB! Use Native S3 locking for Terraform State
  • How to Bring and Manage Manually Created AWS Resources Under Terraform Management
  • Iterating Cloud Resource Provisioning Using Terraform Count and For_Each Meta-Arguments
  • Terraform and Ansible Collaboration for AWS Cloud Deployment

Recent Comments

  1. Charanjit Singh on Terraform and Ansible Collaboration for AWS Cloud Deployment
  2. christinatodd2020aeaa798563 on Terraform and Ansible Collaboration for AWS Cloud Deployment
  3. Charanjit Singh on How to Set password policy in CentOS or RHEL system
  4. SAURABH on How to recover or rebuild initramfs in CentOS 7 Linux
  5. Sangita on How to Set password policy in CentOS or RHEL system

Archives

  • April 2025
  • February 2025
  • January 2025
  • August 2024
  • July 2024
  • June 2024
  • January 2024
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • September 2022
  • August 2022
  • July 2020
  • May 2020
  • February 2020
  • November 2019
  • June 2019
  • May 2019
  • March 2019
  • February 2019
  • December 2018
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • June 2018
  • May 2018
  • April 2018

Categories

  • Automation
  • Cloud Computing
  • Coding
  • CyberSecurity
  • Networking
  • OpenSource
  • RHEL-CentOS
  • Server Hardware
  • SLES
  • Technical Blog
  • Ubuntu
  • WSL

Blog Stats

  • 18,353 hits
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy
  • Home
  • About Me!
  • Way to my Technical Blog
  • Contact me
  • Privacy Policy
© 2025 Welcome To Charanjit Cheema Blog | Powered by Superbs Personal Blog theme
 

Loading Comments...
 

    %d