back to DevOps

12 minute read

Devops as a coders - IaC

Devops as a coders - IaC

Infrastructure as Code (IaC) is an approach to infrastructure automation where infrastructure is defined in a code format, typically using a high-level language, and managed in a version-controlled repository. IaC enables you to automate the provisioning and management of infrastructure resources such as servers, networks, databases, and storage, in a way that is reproducible, scalable, and consistent.

By using IaC, you can define your infrastructure as code, which means you can create, update, and destroy your infrastructure using code. This can be done through configuration files, scripts, templates, or other code-based tools. With IaC, you can easily manage and automate the infrastructure, and the changes can be version-controlled and tracked over time.

IaC can be used with different tools and services, such as Terraform and CloudFormation. These tools enable you to define infrastructure resources in code, deploy and manage them automatically, and integrate with other tools in your development and operations workflows.

Other helpful tools that help maintain the infrastructure are Ansible and Puppet. The correct group name for these tools is “Configuration Management”, not IaC. This is because these tools use configuration files unlike more complex, multi-file terraform style projects.

Overall, IaC is a powerful approach to automating infrastructure management, reducing manual errors, and improving collaboration between development and operations teams.

Terraform

Terraform is an open-source infrastructure as code (IaC) tool that enables you to manage and automate the deployment of infrastructure resources in a cloud environment. It is developed by HashiCorp, the same company behind other popular DevOps tools such as Vagrant, Packer, and Consul.

Terraform allows you to define your infrastructure in a code format using a declarative language called HashiCorp Configuration Language (HCL) or JSON. With Terraform, you can create and manage resources such as virtual machines, load balancers, databases, and networks, across multiple cloud platforms like AWS, Azure, Google Cloud Platform, and others.

One of the key features of Terraform is its ability to create a dependency graph of your infrastructure resources and manage them in a safe and efficient way. Terraform can also be integrated with other DevOps tools like Ansible, Chef, and Puppet, enabling you to create more comprehensive automation workflows.

Overall, Terraform is a powerful tool for infrastructure automation that enables you to manage your infrastructure as code, providing greater control, consistency, and scalability of your infrastructure resources.

 

Here's an example of a basic Terraform project that creates a VPC with EC2 instance.

Dictionary for example:

  • VPC - virtual private cloud.
  • EC2 - virtual private server instance. This is name of vps provided by AWS (Elastic Compute)
  • S3 bucket (Simple Storage Service) - storage service provided by AWS. We will use it to store our terraform configuration state.
  • awscli - aws command line tool. We will need it to enable terraform communication with our aws account. To install it, use official documentation here[https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html]
  • tfstate - terraform state file. It’s created and modified each time we deploy our changes to infrastructure. Most important file, because it’s keeping all info about infrastructure state. Fixing it manually it’s really pain in the ass.
  • ansible playbook - yaml configuration file that contains the instructions to setting up the environment, they are executed top to bottom.

We need to define providers we’ll use in project

providers.tf

provider "aws" {

 region  "eu-central-1"

 profile "myaws"

}

This is the important part. If you’re working with lots of aws configurations, you should use profiles - named configurations. Create your configuration using awscli, and be sure to name it, then put this name in `profile` field in providers. This way, terraform will know what configuration use.

We’ll use s3 bucket as a tfstate storage. It will allow us to have access to our saved config remotely. 

backend.tf

terraform {

 required_version "~> 1.3.7"

 

 backend "s3" {

   bucket  "project-tfstates"

   profile "project"

   key     "infrastructure.tfstate"

   region  "eu-central-1"

   encrypt "true"

 }

}

We want to have our variables stored as clear as we can. So like a professional programmer, keep config separate - use locals and variables files 🙂

locals.tf

locals {

 name     "project"

 tags     = {

   Project     = local.name

   Purpose     "Playing with terraform"

   Terraformed "True"

 }

 systems = {

   amazonlinux "ami-0dcc0ebde7b2e00db"

 }

 environment "PLAYGROUND"

}

variables.tf

variable "default_ssh_range" {

 type    = list(string)

 default = ["123.123.123.123/32""321.321.321.321/32"]

}

Here we’re defining sample ip’s to whitelist for security group to access machine via ssh. Change this to match your ip.

vpc.tf:

module "vpc" {

 source  "terraform-aws-modules/vpc/aws"

 version "3.13.0"

 

 name                  "${local.name}-vpc"

 private_subnet_suffix "private"

 private_subnet_tags   = { "Name" "${local.name}-subnet-private" }

 public_subnet_tags    = { "Name" "${local.name}-subnet-public" }

 cidr                  "10.100.0.0/16"

 

 azs             = ["eu-central-1a""eu-central-1b""eu-central-1c"]

 private_subnets = ["10.100.0.0/20""10.100.16.0/20""10.100.32.0/20"]

 public_subnets  = ["10.100.208.0/20""10.100.224.0/20""10.100.240.0/20"]

 

 enable_nat_gateway         false

 manage_default_network_acl true

 default_network_acl_tags   = { Name "${local.name}-default" }

 

 manage_default_route_table true

 default_route_table_tags   = { Name "${local.name}-default" }

 

 enable_flow_log                      true

 create_flow_log_cloudwatch_log_group true

 create_flow_log_cloudwatch_iam_role  true

 flow_log_max_aggregation_interval    60

 

 enable_dns_hostnames true

 enable_dns_support   true

 

 tags = local.tags

}

 

module "sg_ssh" {

 source  "terraform-aws-modules/security-group/aws"

 version "4.9.0"

 

 name        "${local.name}-ssh"

 description "Allow SSH traffic from mihurocks IPs"

 vpc_id      = module.vpc.vpc_id

 

 ingress_cidr_blocks = var.default_ssh_range

 ingress_rules       = ["ssh-tcp"]

 

 egress_cidr_blocks = ["0.0.0.0/0"]

 egress_rules       = ["all-all"]

 

 tags = local.tags

}

 

module "sg_web" {

 source  "terraform-aws-modules/security-group/aws"

 version "4.9.0"

 

 name        "${local.name}-web"

 description "Allow all HTTP/HTTPS traffic"

 vpc_id      = module.vpc.vpc_id

 

 ingress_cidr_blocks = ["0.0.0.0/0"]

 ingress_rules       = ["http-80-tcp""https-443-tcp"]

 

 tags = local.tags

}

 

ec2.tf

 

module "ec2" {

 source                      "terraform-aws-modules/ec2-instance/aws"

 version                     "~> 3.0"

 name                        "${local.environment} machine"

 ami                         = local.systems.amazonlinux

 instance_type               "t2.micro"

 monitoring                  true

 subnet_id                   = element(local.public_subnets0)

 vpc_security_group_ids      = [local.sg_ssh_idlocal.sg_web_id]

 user_data                   = file("globals/synchronize_ssh_keys.sh")

 associate_public_ip_address true

 tags                        = local.tags

}

 

resource "aws_eip" "ec2_eip" {

 vpc  true

 tags = { Name "${local.environment} machine" }

}

 

resource "aws_eip_association" "ec2_assoc" {

 instance_id   = module.ec2.id

 allocation_id aws_eip.ec2_eip.id

}

here I’m using user data attribute to setup my public key for root user. Sample bash script to do that:

#!/bin/bash

echo "ssh-rsa yor-public-ssh-key" >> /root/.ssh/authorized_keys

EOF

 

To run this project, you would follow these steps:

  1. Install Terraform
  2. Install aws cli
  3. Create the a new project directory, place files listed there
  4. Initialize the directory by running terraform init
  5. Preview the changes by running terraform plan
  6. Apply the changes by running terraform apply

Terraform will then create the AWS VPC + EC2 instance according to the configuration defined in the project.

Note that this is a very basic example, and in practice, you would typically define many more resources and use variables, modules, and other Terraform features to manage a more complex infrastructure.

Ansible

Ansible is an open-source configuration management and orchestration tool that enables you to automate the deployment and management of infrastructure resources. It was developed by Red Hat and is now maintained by the Ansible community.

With Ansible, you can define the desired state of your infrastructure resources, such as servers, applications, and network configurations, using a declarative language called YAML. Ansible uses SSH or WinRM to connect to target machines and apply the configuration changes. Ansible does not require a central server to manage the configurations; instead, it uses a push-based model where you can run playbooks on-demand.

Ansible enables you to:

  • Automate infrastructure management: You can automate the deployment and management of software and system configurations, reducing manual errors and saving time.
  • Enforce policy and compliance: You can enforce policies and compliance requirements across your infrastructure, ensuring that your systems are secure and compliant.
  • Scale infrastructure management: You can easily manage large-scale infrastructures, and Ansible is designed to work well in complex and heterogeneous environments.
  • Integrate with other tools: Ansible can be integrated with other DevOps tools such as Jenkins, Docker, Kubernetes, and more.

Ansible is a popular tool for managing infrastructure resources and is used by companies across various industries, including finance, healthcare, technology, and more.

Below example of a basic Ansible project that installs the Nginx web server on a group of Centos  servers:

hosts:

[webservers]
web1.example.com
web2.example.com
web3.example.com

 

playbook.yml:

- hosts: webservers
  become: true
  tasks:
    - name: Install Nginx web server
      yum:
        name: nginx
        state: present
      notify:
        - start nginx service

  handlers:
    - name: start nginx service
      service:
        name: nginx
        state: started

 

In this example, we first define a group of servers called "webservers" in the hosts file. Then, we define a playbook that runs on the webservers group, installs Apache using the "apt" module, and starts the Apache service using a handler. The "become: true" statement is used to escalate privileges to run the tasks as a privileged user.

To run this project, you would follow these steps:

  1. Install Ansible
  2. Create the hosts and playbook.yml files in a new directory
  3. Verify connectivity to the target servers by running ansible webservers -m ping
  4. Run the playbook by running ansible-playbook playbook.yml

Ansible will then connect to the servers defined in the hosts file, install Apache, and start the Apache service according to the configuration defined in the playbook.yml file.

Note that this is a very basic example, and in practice, you would typically define many more tasks and use variables, roles, and other Ansible features to manage a more complex infrastructure.

Join forces

Terraform and Ansible are both tools that can be used to manage infrastructure, but they serve different purposes. Terraform is used to provision and manage infrastructure resources, while Ansible is used for configuration management and application deployment.

That being said, it is possible to use Ansible to automate the deployment of Terraform configurations. You can use the "command" or "shell" module in Ansible to run Terraform commands on the target servers.

Here's an example playbook that uses the "command" module to run Terraform commands:

- hosts: my_servers
  become: true
  tasks:
    - name: Initialize Terraform
      command: terraform init
      args:
        chdir: /path/to/terraform/directory

    - name: Apply Terraform plan
      command: terraform apply -auto-approve
      args:
        chdir: /path/to/terraform/directory

 

In this example, we define a playbook that runs on the "my_servers" group of servers. The "become: true" statement is used to escalate privileges to run the tasks as a privileged user. We then define two tasks: the first task runs the "terraform init" command to initialize the Terraform configuration, and the second task runs the "terraform apply -auto-approve" command to apply the Terraform plan.

To run this playbook, you would run the following command:

ansible-playbook my_playbook.yml -i inventory.ini

 

Note that this is a very basic example, and in practice, you would typically define many more tasks and use variables, roles, and other Ansible features to manage a more complex infrastructure with Terraform.