Skip to content

Latest commit

 

History

History
321 lines (289 loc) · 9.96 KB

deployment-terraform.md

File metadata and controls

321 lines (289 loc) · 9.96 KB

Golang Bookstore ADOT - CI/CD with Terraform AWS CodeBuild, AWS CodePipeline & Amazon SNS

ADOT (AWS Distro for OpenTelemetry) Implementation for Simple Golang RESTful API Application (Bookstore)

goreport all contributors tags docker pulls download all view clone issues pull requests forks stars license


Example CI/CD Terraform Script cicd-terraform.tf

This script includes:

  • ECR repository creation
  • SNS topic creation and subscription
  • CodeBuild project creation for production and staging environments
  • CodePipeline creation with four stages: Source, Build, ManualApproval, and Deploy (two times)
  • ECS task definition and service creation for staging environment
  • CloudWatch Event rule and target for manual approval notification
  • Outputs for the staging and production URLs, build number, and semantic versions for staging and production.
  • Using custom image AWS CodeBuild devopscorner/cicd:codebuild-4.0
### Golang Terraform Pipeline (GitHub, AWS CodeBuild, AWS Pipeline, Amazon SNS) ###

# This script includes:
#  - ECR repository creation
#  - SNS topic creation and subscription
#  - CodeBuild project creation for production and staging environments
#  - CodePipeline creation with four stages: Source, Build, ManualApproval, and Deploy (two times)
#  - ECS task definition and service creation for staging environment
#  - CloudWatch Event rule and target for manual approval notification
#  - Outputs for the staging and production URLs, build number, and semantic versions for staging and production.
#  - Using custom image AWS CodeBuild `devopscorner/cicd:codebuild-4.0`

terraform {
  required_version = ">= 1.0.9"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 3.63.0, < 4.0"
    }
  }
}

provider "aws" {
  region = "us-west-2"
}

data "aws_caller_identity" "current" {}

resource "aws_ecr_repository" "bookstore-adot" {
  name = "devopscorner/bookstore-adot"
}

resource "aws_sns_topic" "bookstore-adot-topic" {
  name = "bookstore-adot-topic"
}

resource "aws_sns_topic_subscription" "bookstore-adot-subscription" {
  topic_arn = aws_sns_topic.bookstore-adot-topic.arn
  protocol  = "email"
  endpoint  = "[email protected]"
}

resource "aws_codebuild_project" "bookstore-adot-prod" {
  name          = "bookstore-adot-prod"
  description   = "My Golang app for production"
  service_role  = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole"
  build_timeout = "60"
  artifacts {
    type = "NO_ARTIFACTS"
  }
  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    # image      = "aws/codebuild/standard:5.0"
    image = "devopscorner/cicd:codebuild-4.0"
    type  = "LINUX_CONTAINER"
    environment_variable {
      name  = "AWS_REGION"
      value = "us-west-2"
    }
    environment_variable {
      name  = "ENVIRONMENT"
      value = "prod"
    }
    environment_variable {
      name  = "ECR_REPOSITORY_URI"
      value = aws_ecr_repository.bookstore-adot.repository_url
    }
  }
  source {
    type            = "GITHUB"
    location        = "https://github.com/devopscorner/golang-adot"
    git_clone_depth = 1
    buildspec       = file("${path.module}/buildspec.yaml")
    auth {
      type = "OAUTH"
    }
  }
}

resource "aws_codebuild_project" "bookstore-adot-staging" {
  name          = "bookstore-adot-staging"
  description   = "My Golang app for staging"
  service_role  = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodeBuildServiceRole"
  build_timeout = "60"
  artifacts {
    type = "NO_ARTIFACTS"
  }
  environment {
    compute_type = "BUILD_GENERAL1_SMALL"
    # image      = "aws/codebuild/standard:5.0"
    image = "devopscorner/cicd:codebuild-4.0"
    type  = "LINUX_CONTAINER"
    environment_variable {
      name  = "AWS_REGION"
      value = "us-west-2"
    }
    environment_variable {
      name  = "ENVIRONMENT"
      value = "staging"
    }
    environment_variable {
      name  = "ECR_REPOSITORY_URI"
      value = aws_ecr_repository.bookstore-adot.repository_url
    }
  }
  source {
    type            = "GITHUB"
    location        = "https://github.com/devopscorner/golang-adot"
    git_clone_depth = 1
    buildspec       = file("${path.module}/buildspec.yaml")
    auth {
      type = "OAUTH"
    }
  }
}

resource "aws_codepipeline" "bookstore-adot" {
  name     = "bookstore-adot"
  role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/CodePipelineServiceRole"
  artifact_store {
    type     = "S3"
    location = "your-artifact-store-bucket"
    encryption_key {
      type = "KMS"
      id   = "your-kms-key-id"
    }
  }

  stage {
    name = "Source"
    action {
      name             = "Source"
      category         = "Source"
      owner            = "AWS"
      provider         = "GitHub"
      version          = "1"
      output_artifacts = ["SourceArtifact"]
      configuration = {
        Owner      = "your-username"
        Repo       = "your-repo"
        Branch     = "main"
        OAuthToken = "your-github-token"
      }
    }
  }

  stage {
    name = "Build"
    actions {
      name             = "Build"
      category         = "Build"
      owner            = "AWS"
      provider         = "CodeBuild"
      version          = "1"
      input_artifacts  = ["SourceArtifact"]
      output_artifacts = ["BuildArtifact"]
      configuration = {
        ProjectName = aws_codebuild_project.bookstore-adot-staging.name
      }
    }
  }

  stage {
    name = "ManualApproval"
    actions {
      name            = "ManualApproval"
      category        = "Approval"
      owner           = "AWS"
      provider        = "Manual"
      version         = "1"
      input_artifacts = ["BuildArtifact"]
      configuration = {
        NotificationArn = aws_sns_topic.bookstore-adot-topic.arn
        CustomData      = "Do you want to deploy the latest changes to production?"
      }
    }
  }

  stage {
    name = "Deploy-Staging"
    actions {
      name            = "Deploy-Staging"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      version         = "1"
      input_artifacts = ["BuildArtifact"]
      configuration = {
        ClusterName = "bookstore-adot-cluster"
        ServiceName = "bookstore-adot-staging"
        FileName    = "imagedefinitions.json"
        ImageUri    = "${aws_ecr_repository.bookstore-adot.repository_url}:${var.semver_staging}"
      }
    }
  }

  stage {
    name = "Deploy-Prod"
    actions {
      name            = "Deploy-Prod"
      category        = "Deploy"
      owner           = "AWS"
      provider        = "ECS"
      version         = "1"
      input_artifacts = ["BuildArtifact"]
      configuration = {
        ClusterName = "bookstore-adot-cluster"
        ServiceName = "bookstore-adot-prod"
        FileName    = "imagedefinitions.json"
        ImageUri    = "${aws_ecr_repository.bookstore-adot.repository_url}:${var.semver_prod}"
      }
      condition {
        type     = "StartsWith"
        variable = "ApprovalStatus"
        value    = "Approved"
      }
    }
  }
}

resource "aws_ecs_task_definition" "bookstore-adot" {
  family = "bookstore-adot"
  container_definitions = jsonencode([{
    name               = "bookstore-adot"
    image              = "${aws_ecr_repository.bookstore-adot.repository_url}:${var.semver_staging}"
    cpu                = 512
    memory_reservation = 512
    port_mappings = {
      container_port = 8080
      host_port      = 0
    }
  }])
}

resource "aws_ecs_service" "bookstore-adot-staging" {
  name            = "bookstore-adot-staging"
  cluster         = "bookstore-adot-cluster"
  task_definition = aws_ecs_task_definition.bookstore-adot.arn
  desired_count   = 2
  launch_type     = "EC2"
  load_balancer {
    target_group_arn = "your-target-group-arn"
    container_name   = "bookstore-adot"
    container_port   = 8080
  }
}

resource "aws_cloudwatch_event_rule" "bookstore-adot-manual-approval" {
  name        = "bookstore-adot-manual-approval"
  description = "Manual approval rule for my Golang app"
  event_pattern = jsonencode({
    source      = ["aws.codepipeline"]
    detail_type = ["CodePipeline Action Execution State Change"]
    detail = {
      pipeline = ["${aws_codepipeline.bookstore-adot.name}"]
      stage    = ["ManualApproval"]
      action   = ["ManualApproval"]
      state    = ["STARTED", "SUCCEEDED", "FAILED"]
    }
  })
}

resource "aws_cloudwatch_event_target" "bookstore-adot-manual-approval" {
  rule = aws_cloudwatch_event_rule.bookstore-adot-manual-approval.name
  arn  = aws_sns_topic.bookstore-adot-topic.arn
}

output "bookstore-adot-staging-url" {
  value = aws_alb.bookstore-adot-staging.dns_name
}

output "bookstore-adot-prod-url" {
  value = aws_alb.bookstore-adot-prod.dns_name
}

output "bookstore-adot-build-number" {
  value = aws_codebuild_project.bookstore-adot-staging.build_number
}

output "bookstore-adot-semver-staging" {
  value = var.semver_staging
}

output "bookstore-adot-semver-prod" {
  value = var.semver_prod
}