Terraform and Ansible: Teaming Up for Automated Cloud Magic
Tags: terraform, ansible, azure, devops
Published: 2023-08-17 15:44:04

## TL;DR

In the world of cloud automation, Terraform and Ansible form a seamless partnership. Terraform constructs infrastructure, while Ansible configures it. Leveraging dynamic inventories and GitHub Actions, the process gains efficiency.

Explore my [GitHub repository](https://github.com/ishuar/terraform-ansible-azure) for hands-on experience. Delve into Terraform's provisioning, Ansible's management, and GitHub's orchestration.

*Unlock cloud automation with Terraform, Ansible, and GitHub's synergy.*

## Introduction

In the dynamic world of cloud computing, provisioning infrastructure and managing configurations are essential tasks. This is where Terraform and Ansible come into play, acting as a dynamic duo that enables you to orchestrate automated cloud magic.

## The Power of Terraform and Ansible
<br>
### Terraform: Infrastructure Provisioning Simplified

Terraform stands as a powerful infrastructure-as-code tool. It allows you to define your cloud infrastructure using a human-readable syntax. This approach streamlines the process of spinning up resources on cloud platforms like Azure. Whether it's virtual machines, networking components, or databases, Terraform's declarative approach ensures consistent provisioning across environments.

### Ansible: Configuration Management Perfected

On the other hand, Ansible specializes in configuration management. It allows you to define the desired state of your servers and applications. Ansible playbooks, written in simple YAML syntax, automate the process of configuring servers, installing software, and ensuring consistency across your infrastructure. This comes in handy when you're dealing with tasks like setting up web servers or managing security configurations.

## Complementary, Not Competitive

### Dispelling the Misconception

One common misconception is that Terraform and Ansible compete with each other. In reality, they are highly complementary. Terraform focuses on creating and destroying resources, while Ansible excels in configuring and maintaining those resources. This synergy ensures that your infrastructure is not just provisioned but also tailored to meet your specific requirements.

## Dynamic Inventories and Pipeline Automation

### Dynamic Inventories: A Game Changer

A remarkable feature that enhances this collaboration is the use of dynamic inventories. Instead of maintaining static inventory lists, Ansible can directly fetch information about your cloud resources from the likes of Azure using dynamic inventory plugins. This makes your playbooks flexible and adaptable to the evolving cloud landscape.

### Seamless Automation with GitHub Actions

Bringing it all together, GitHub Actions empowers you to automate your workflows. With GitHub as your source version control, you can leverage GitHub Actions to define pipelines that seamlessly integrate Terraform and Ansible. Pushing code triggers the orchestration of provisioning infrastructure and configuring it, all without manual intervention.

## Embarking on Practical Cloud Journey

### Hands-On Learning

For those eager to dive into practical knowledge, there's a treasure trove awaiting you. Inside the [GitHub repository](https://github.com/ishuar/terraform-ansible-azure), you'll find a rich collection of code that practically demonstrates the synergy between Terraform and Ansible. Each line of code showcases how to orchestrate cloud resources and configure them seamlessly.

{% embed https://github.com/ishuar/terraform-ansible-azure %}

By immersing yourself in this repository, you're not just reading about automation; you're experiencing it firsthand. Through tinkering, testing, and exploration, you'll uncover the magic that comes to life when Terraform and Ansible work in harmony.

A glimpse of the tools and components involved is as shown below

![Architecture Diagram, where developer push code to github, github action using terraform provision azure infrastructure afterwards github action using ansible configures the azure virtual machines](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuqglxzntg3nseiq5cm3.gif)
<br>

### Terraform as your spell book 📓

First thing first, please refer to Readme file within the `terraform/linux-webserver-with-loadbalancer` directory for pre-requisites to replicate the infrastructure on your local environment.

> **INFO:** For best experience open all embedded links in a new browser window/tab 💻.

- {% cta https://github.com/ishuar/terraform-ansible-azure/blob/main/terraform/linux-webserver-with-loadbalancer/local.tf %}local.tf{% endcta %} 
<br>

- {% cta https://github.com/ishuar/terraform-ansible-azure/blob/main/terraform/linux-webserver-with-loadbalancer/variables.tf %}variables.tf{% endcta %}
<br>

- {% cta https://github.com/ishuar/terraform-ansible-azure/blob/main/terraform/linux-webserver-with-loadbalancer/dependencies.tf %}Dependencies{% endcta %}
<br>

- {% cta https://github.com/ishuar/terraform-ansible-azure/blob/main/terraform/linux-webserver-with-loadbalancer/azure-load-balancer.tf %}Azure Load Balancer{% endcta %}
<br>

- {% cta https://github.com/ishuar/terraform-ansible-azure/blob/main/terraform/linux-webserver-with-loadbalancer/linux-virtual-machines.tf %}Azure Linux Virtual Machines{% endcta %}
 
### Ansible as your ancient scroll 📜

First thing first, please refer to Readme file within the `ansible` directory for pre-requisites to replicate the infrastructure on your local environment.

- **Ansible Configuration.**

```yaml

##? Generate complete using: ansible-config init --disabled -t all > <path>/ansible.cfg

[defaults]

# (boolean) Set this to "False" if you want to avoid host key checking by the underlying tools Ansible uses to connect to the host
host_key_checking = False
force_color = True

# (integer) Port to use in remote connections, when blank it will use the connection plugin default.
## As we have changed the default SSH port of our VMs
remote_port=8822

[privilege_escalation]

# (boolean) Toggle to prompt for privilege escalation password.
become_ask_pass=False

# (string) Privilege escalation method to use when `become` is enabled.
become_method=sudo

# (string) The user your login/remote user 'becomes' when using privilege escalation, most systems will use 'root' when no user is specified.
become_user=root

```

- **Dynamic Inventory**


Refer to [pre-requisites](https://github.com/ishuar/terraform-ansible-azure/blob/main/ansible/README.md#local-development-environment) for local set environment set up.

```yaml
---
plugin: azure_rm

include_vm_resource_groups:
  - ansible-vm-resources

auth_source: auto
conditional_groups:
  # since this will be true for every host, every host sourced from this inventory plugin config will be in the
  # group 'all_the_hosts'
  all_the_hosts: true

# places hosts in dynamically-created groups based on a variable value.
keyed_groups:
  # places each host in a group named 'tag_(tag name)_(tag value)' for each tag on a VM.
  # - prefix: tag
  #   key: tags
  # places each host in a group named 'azure_loc_(location name)', depending on the VM's location
  - prefix: azure_loc
    key: location
  # places host in a group named 'some_tag_X' using the value of the 'sometag' tag on a VM as X, and defaulting to the
  # value 'none' (eg, the group 'some_tag_none') if the 'sometag' tag is not defined for a VM.
  - prefix: role
    key: tags.role | default('none')

```

- [**`Ansible Roles Involved`**](https://github.com/ishuar/terraform-ansible-azure/tree/main/ansible/roles)

- **Ansible Playbook**

```yaml
---
- name: Set up Nginx Webserver on Ubuntu machine
  gather_facts: true
  remote_user: adminuser
  hosts: "{{ dynamic_hosts }}"
  become: true
  connection: ssh
  pre_tasks: []
  vars:
    dynamic_hosts: role_slave_webservers

  roles:
    - role: azure_vm_ufw
      when: enable_firewall | bool
    - role: nginx_webserver

```

### Realm of GitHub Actions 🪐

Concept of [Github reusable workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows) are utilised in the repository, hence create workflow one time and then can re-use it for supporting used case.

**Reusable Workflows**

- [`terraform-infra-set-up.yaml`](https://github.com/ishuar/terraform-ansible-azure/blob/main/.github/workflows/terraform-infra-set-up.yaml)

- [`ansible-set-up.yaml`](https://github.com/ishuar/terraform-ansible-azure/blob/main/.github/workflows/ansible-set-up.yaml)

**Deployment and Configuration Workflows**

- `webservers-infra-terraform.yaml`

```yaml
name: "Create Webservers Infrastructure"
on:
  workflow_dispatch:
    inputs:
      terraform-version:
        type: number
        required: false
        default: 1.5.4
        description: The terraform version used for the github action.

      cache-hash-file:
        type: string
        required: false
        default: '/providers.tf'
        description: The file used to create common hash cache naming.
  push:
      branches:
        - main
      paths:
      - "terraform/**"
      - ".github/workflows/terraform-infra-set-up.yaml"
      - ".github/workflows/webservers-infra-terraform.yaml"

  pull_request:
    paths:
    - "terraform/**"
    - ".github/workflows/terraform-infra-set-up.yaml"
    - ".github/workflows/webservers-infra-terraform.yaml"

concurrency:
  group: terraform-webservers

jobs:
  webserversInfra:
    name: Create infrastructure for webservers
    uses: ./.github/workflows/terraform-infra-set-up.yaml
    with:
      terraform-dir: "terraform/linux-webserver-with-loadbalancer"
      terraform-version: ${{ inputs.terraform-version != '' && inputs.terraform-version || vars.TERRAFORM_VERSION }}
    secrets: inherit
```

- `webservers-config-ansible.yaml`

```yaml
name: "Configure Nginx Webservers in Ubuntu via Ansible"
on:
  workflow_dispatch:
  push:
    branches:
      - main
    ## in Case push to main by codeowners
    paths:
    - "ansible/**"
    - ".github/workflows/set-up-ubuntu-nginx-webserver.yaml"
    - ".github/workflows/ansible-set-up.yaml"

  pull_request:
    paths:
    - "ansible/**"
    - ".github/workflows/set-up-ubuntu-nginx-webserver.yaml"
    - ".github/workflows/ansible-set-up.yaml"

concurrency:
  group: ansible-webservers

jobs:
  webserversConfig:
    name: Configure Nginx webservers
    uses: ./.github/workflows/ansible-set-up.yaml
    with:
      playbook: set-up-ubuntu-nginx-webserver.yaml
      terraform-output-directory: terraform/linux-webserver-with-loadbalancer
      nsg-ssh-port: 8822
    secrets:
      ssh-private-key: ${{ secrets.PASSWORDLESS_SSH_PRIVATE_KEY }}
      AZURE_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
      AZURE_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
      AZURE_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
      AZURE_SUBSCRIPTION_ID: ${{ vars.ARM_SUBSCRIPTION_ID }}
```

## Wrapping Up

Terraform and Ansible are more than just tools; they represent a collaborative approach to cloud automation. By using Terraform for infrastructure provisioning and Ansible for configuration management, you unlock a potent synergy that ensures your cloud environment is both robust and adaptable. Add dynamic inventories and GitHub Actions into the mix, and you have a recipe for automated cloud magic that simplifies and streamlines your operations.

Happy automating!

### Thank You 🙏

A heartfelt thank you and grateful for the time you've spent for reading the article. I hope I was able to ignite your curiosity and guided you through the realm of Terraform, Ansible, and cloud magic.

**Your thoughts matter!** 

If this journey sparked ideas or questions, I'd love to hear from you. Share your feedback, suggestions via [GitHub Issue](https://github.com/ishuar/terraform-ansible-azure/issues) or even a magical star ⭐️  for the project on GitHub.

---
*Stay tuned for more tech insights.*

{% embed https://github.com/ishuar/terraform-ansible-azure %}

>> **Guess what?** 

This document got a little boost from AI magic, making it even more exciting. But don't worry, it's still your friendly guide, here to help you on your learning journey