Provisioning Infrastructure on AWS cloud with Terraform and Azure DevOps.

Provisioning Infrastructure on AWS cloud with Terraform and Azure DevOps.

DevOps is an organizational model that combines development and operations teams to enable quicker application development and more straightforward maintenance of current installations. DevOps promotes shorter, more controlled iterations by assisting firms in strengthening relationships between Dev, Ops, and other firm stakeholders through the adoption of best practices, automation, and new tools. This project aims at creating a solution for a company that uses DevOps practices to improve its software lifecycle. They want a solution that will perform orchestration from one cloud to another using this DevOps Pipeline.

62d0f091fc73b7a5828854b1_022021-Harness-Blogpost-DevOpsGeneric-Header-2400x700-1.png

BUSINESS NEED

This solution aims at a use case where a company uses AWS and Azure as its cloud providers. This company uses Azure DevOps to manage its software lifecycle. They do not want to use Jenkins or Team City to build or orchestrate their infrastructure; instead, they want to leverage Terraform to provision their infrastructure using automation.

SOLUTION

To implement this use case, we need to create an infrastructure as code using Terraform, where we will provision multiple AWS services (EC2, S3, Security Group). We will be automating the infrastructure development setup for AWS using the Azure DevOps Pipeline. We will also use S3 buckets to store Terraform state information, and DynamoDB tables will provide lock capability while performing operations. Our code repository will be GitHub, and we will link it with the Azure DevOps Pipeline.

CI/CD Continuous Integration & Deployment with DevOps

CI/CD Architecture

Using Azure DevOps, we create various jobs, which will be initiated as soon as we make a release. Each job will define multiple stages of Terraform states such as "init," "planning," "applying," and "destroying." After these jobs are run, another job will deploy the services inside AWS. After the project is complete, another release will delete the resources from AWS.

Security Architecture

Security Architecture

From a security perspective, the data coming from the VCS (GitHub) will be encrypted by Azure. Azure allows us to integrate different VCSs into its DevOps platform. We must sign in using our GitHub credentials to establish a secure connection.

When the code is executed in Azure, the deployment will be carried out on AWS. To access the AWS account, the AWS toolkit allows Azure DevOps to sign in to an AWS account with programmatic access (Access Key & Secret Key)

Solution Architecture

Solution Architecture

The DevOps team will access the Terraform code from GitHub and create jobs inside the release pipeline.

backend.tf

terraform {
  backend "s3" {
    bucket = "mydev-tf-state-bucket"
    key = "main"
    region = "us-east-2"
    dynamodb_table = "my-dynamodb-table"
  }
}

Jenkinsfile.tf

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage ("terraform init") {
            steps {
                sh ("terraform init -reconfigure") 
            }
        }

        stage ("plan") {
            steps {
                sh ('terraform plan') 
            }
        }

        stage (" Action") {
            steps {
                echo "Terraform action is --> ${action}"
                sh ('terraform ${action} --auto-approve') 
           }
        }
    }
}

main.tf

provider "aws" {
  region = var.aws_region
}

resource "aws_vpc" "main" {
  cidr_block = "172.16.0.0/16"
  instance_tenancy = "default"
  tags = {
    Name = "main"
  }
}

#Create security group with firewall rules
resource "aws_security_group" "jenkins-sg-2022" {
  name        = var.security_group
  description = "security group for Ec2 instance"

  ingress {
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

 ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

 # outbound from jenkis server
  egress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags= {
    Name = var.security_group
  }
}

resource "aws_instance" "myFirstInstance" {
  ami           = var.ami_id
  key_name = var.key_name
  instance_type = var.instance_type
  vpc_security_group_ids = [aws_security_group.jenkins-sg-2022.id]
  tags= {
    Name = var.tag_name
  }
}

# Create Elastic IP address
resource "aws_eip" "myFirstInstance" {
  vpc      = true
  instance = aws_instance.myFirstInstance.id
tags= {
    Name = "my_elastic_ip"
  }
}

s3.tf

resource "aws_s3_bucket" "my-s3-bucket" {
  bucket_prefix = var.bucket_prefix
  acl = var.acl

   versioning {
    enabled = var.versioning
  }

  tags = var.tags
}

sonar.tf

resource "aws_instance" "mySonarInstance" {
      ami           = "ami-0b9064170e32bde34"
      key_name = var.key_name
      instance_type = "t2.micro"
      vpc_security_group_ids = [aws_security_group.sonar-sg-2022.id]
      tags= {
        Name = "sonar_instance"
      }
    }

 resource "aws_security_group" "sonar-sg-2022" {
      name        = "security_sonar_group_2022"
      description = "security group for Sonar"

      ingress {
        from_port   = 9000
        to_port     = 9000
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }

     ingress {
        from_port   = 22
        to_port     = 22
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }

     # outbound from Sonar server
      egress {
        from_port   = 0
        to_port     = 65535
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }

      tags= {
        Name = "security_sonar"
      }
    }

# Create Elastic IP address for Sonar instance
resource "aws_eip" "mySonarInstance" {
  vpc      = true
  instance = aws_instance.mySonarInstance.id
tags= {
    Name = "sonar_elastic_ip"
  }
}

variables.tf

variable "aws_region" {
       description = "The AWS region to create things in." 
       default     = "us-east-2" 
}

variable "key_name" { 
    description = " SSH keys to connect to ec2 instance" 
    default     =  "myJune2021Key" 
}

variable "instance_type" { 
    description = "instance type for ec2" 
    default     =  "t2.micro" 
}

variable "security_group" { 
    description = "Name of security group" 
    default     = "jenkins-sgroup-dec-2021" 
}

variable "tag_name" { 
    description = "Tag Name of for Ec2 instance" 
    default     = "my-ec2-instance" 
} 
variable "ami_id" { 
    description = "AMI for Ubuntu Ec2 instance" 
    default     = "ami-020db2c14939a8efb" 
}
variable "versioning" {
    type        = bool
    description = "(Optional) A state of versioning."
    default     = true
}
variable "acl" {
    type        = string
    description = " Defaults to private "
    default     = "private"
}
variable "bucket_prefix" {
    type        = string
    description = "(required since we are not using 'bucket') Creates a unique bucket name beginning with the specified prefix"
    default     = "my-s3bucket-"
}
variable "tags" {
    type        = map
    description = "(Optional) A mapping of tags to assign to the bucket."
    default     = {
        environment = "DEV"
        terraform   = "true"
    }
}

As the Terraform code is executed, the Terraform state will be stored inside an S3 bucket that is already created; it uses a LockID in DynamoDB. The provisioning of resources is carried out on the AWS account.

Install Terraform Task on Azure DevOps Pipeline

Terraform INIT stage on Azure DevOps Pipeline

Terraform Plan stage on Azure DevOps Pipeline

Terraform Apply stage on Azure DevOps Pipeline

After this release pipeline is launched, a Windows server is assigned, and all the tasks will be executed. It will initialize the job, download artifacts, install Terraform, run Terraform init, plan, apply, and finish the job.

After all, this has been completed, and the resources will be created on your AWS Console.