Deployment

CI/CD

In this section, you'll set up CI (Continuous Integration) secrets for GitHub using Terraform. This guide will help you create an IAM user with the necessary permissions for CI tasks and store these credentials securely in your GitHub repository.

Step 1: Create a CI IAM User

The first step is to create an IAM user with the necessary permissions for CI tasks. This user will have access keys that need to be added to your GitHub repository.

module "ci_iam" {
  source  = "../modules/user-with-access-key"
  
  name = "test-staging-ci"
  
  groups = [
    module.cluster.deployment_group,
    module.service.deployment_group,
    module.ecr.deployment_group
  ]
  
  depends_on = [
    module.ecr
  ]
}

What This Does:

  • Creates an IAM User: The ci_iam module sets up an IAM user specifically for CI tasks.
  • Assigns Permissions: The user is added to IAM groups that grant necessary permissions to deploy to ECS, interact with ECR, and more.
  • Ensures Dependencies: The depends_on ensures that the ECR module is created before this IAM user, avoiding potential issues.

Outputs

After running this module, you'll receive the user's access key ID (key_id) and secret access key (key_secret). These are crucial for your CI pipeline.

Step 2: Attach a Custom IAM Policy

Next, you'll attach a custom IAM policy to the CI user, granting it specific permissions required for accessing resources like SSM parameters.

resource "aws_iam_user_policy_attachment" "ci_user" {
  user       = module.ci_iam.name
  policy_arn = aws_iam_policy.ci_user.arn
}

What This Does:

  • Attaches a Policy: This attaches a pre-defined policy to the CI user, which controls what actions the user can perform.

Step 3: Define the IAM Policy

Now, you'll define the custom IAM policy that the CI user will need. This policy grants access to SSM parameters that store secrets and other configuration data.

resource "aws_iam_policy" "ci_user" {
  name   = "staging-apps-ci-ssm"
  policy = data.aws_iam_policy_document.ci_user.json
}

What This Does:

  • Creates a Policy: Defines a custom policy that allows access to specific SSM parameters and describes those parameters.

Step 4: Specify IAM Policy Document

You'll specify the actions and resources that the IAM policy allows. This is done through the aws_iam_policy_document data source.

data "aws_iam_policy_document" "ci_user" {
  statement {
    actions = [
      "ssm:GetParameter",
      "ssm:GetParametersByPath"
    ]
    resources = ["arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/test/staging/api/*"]
  }
  statement {
    effect = "Allow"
    actions = [
      "ssm:DescribeParameters"
    ]
    resources = ["*"]
  }
}

What This Does:

  • Defines Permissions: Specifies exactly what actions (ssm:GetParameter, etc.) the CI user can perform on specific resources.

Step 5: Store CI Secrets

Finally, you'll store the CI user's credentials and other deployment-related information as secrets, which will be used by your GitHub Actions workflow.

module "secrets_ci" {
  source  = "../modules/parameters"
  
  context = {
    namespace = "test"
    stage     = "staging"
    name      = "secrets"
  }
  
  secrets = {
    AWS_ACCESS_KEY_ID     = module.ci_iam.key_id
    AWS_SECRET_ACCESS_KEY = module.ci_iam.key_secret
    ECR_URL               = module.ecr.url
    ECS_CLUSTER           = module.cluster.name
    ECS_SERVICE           = module.service.name
  }
}

What This Does:

  • Stores Secrets Securely: The secrets_ci module securely stores AWS credentials and other necessary information in SSM Parameter Store, making them accessible to your CI pipeline so you can copy them to GitHub.

Connection Summary

  1. CI IAM User Creation: Sets up an IAM user with the required permissions for deploying your application.
  2. Policy Attachment: Links a custom IAM policy to the CI user, allowing it to interact with AWS resources securely.
  3. Secrets Management: Securely stores the IAM user's credentials and other critical data for use in your GitHub Actions workflows.

This setup ensures that your CI pipeline is securely integrated with AWS, providing all necessary permissions to deploy your applications from GitHub.


GitHub Workflows

In this section, you'll set up a GitHub Actions workflow that automates the deployment of your application to Amazon ECS whenever code is pushed to the main branch.

Step 1: Prepare your task-defintion file.

{
    "family": "api-123456",
    "containerDefinitions": [
        {
            "name": "api",
            "image": "",
            "cpu": 1000,
            "memory": 1800,
            "portMappings": [
                {
                    "containerPort": 3000,
                    "hostPort": 0,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "example/staging/api/ecs/api",
                    "awslogs-region": "eu-central-1",
                    "awslogs-stream-prefix": "ecs"
                }
            }
        }
    ],
    "networkMode": "bridge",
    "executionRoleArn": "arn:aws:iam::123456789:role/api-123456-task-execution",
    "taskRoleArn": "arn:aws:iam::123456789:role/api-123456-task",
    "requiresCompatibilities": ["EC2"],
}

To set up and connect a CI/CD action between GitHub and AWS, you need to properly fill in the template using the resource names that were created in the previous steps. Some resources will be filled automatically during the action execution, but most will need to be copied from AWS or manually defined.

  • Family: The family name created when setting up the ECS service
  • Name: You provide this in the action "Fill in the new image ID in the Amazon ECS task definition" or set it to match the previous one
  • Image: The image name is generated during the action
  • CPU: 1024 CPU units are the equivalent of 1 vCPU for Linux
  • Memory: RAM in MB
  • Port mappings: The application ports, with the host port set to 0 as the ports are dynamically forwarded
  • AWS Logs group: The log groups from AWS created during deployment
  • IAM Roles
    • "executionRoleArn": "" - the execution role created during deployment
    • "taskRoleArn": "" - the task role created during deployment

AWS Resources

For values marked with look into your AWS Console

Step 2: Define the GitHub Actions Workflow

The workflow will build your Docker image, push it to ECR, and deploy the new image to ECS.

name: Deploy (Staging)

on:
  push:
    branches:
      - 'main'

jobs:
  build:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.STAGING_AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.STAGING_AWS_SECRET_ACCESS_KEY }}
          aws-region: eu-central-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2.0.1

      - name: Build, tag, and push image to Amazon ECR
        id: build-image
        env:
          ECR_URL: ${{ secrets.STAGING_AWS_ECR_URL }}
          IMAGE_TAG: preview-${{ github.sha }}
        run: |
          docker build -t $ECR_URL:$IMAGE_TAG .
          docker tag $ECR_URL:$IMAGE_TAG $ECR_URL:$IMAGE_TAG
          docker push $ECR_URL:$IMAGE_TAG
          docker push $ECR_URL:$IMAGE_TAG
          echo "image=$ECR_URL:$IMAGE_TAG" >> $GITHUB_OUTPUT

      - name: Render secrets from parameter store
        id: render-secrets
        uses: Selleo/amazon-ecs-render-task-definition-secrets@v1.0.0
        with:
          region: eu-central-1
          task-definition: task-definition.json
          envs: |
            API_KEY
            ENVIRONMENT
            POSTGRES_URL
            REDIS_URL
            PORT
          paths: |
            /test/staging/api/terraform/
            /test/staging/api/editable/

      - name: Fill in the new image ID in the Amazon ECS task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ${{ steps.render-secrets.outputs.task-definition }}
          container-name: api
          image: ${{ steps.build-image.outputs.image }}

      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.task-def.outputs.task-definition }}
          service: api
          cluster: staging-test-api-321a68b3
          wait-for-service-stability: true

What This Does:

  • Triggers on Push: The workflow starts whenever there’s a push to the main branch.
  • Builds and Deploys: It builds a Docker image, pushes it to Amazon ECR, and updates your ECS service with the new image. Certainly! Here's how you can include that in your documentation:

Custom Triggers

Triggers are available for different actions and branches, allowing you to customize when your workflows run. Learn more about setting up custom triggers here.

Step 2: Configure AWS Credentials

Ensure that your GitHub Actions workflow has the necessary AWS credentials, which were set up in the previous section.

Steps:

  • Checkout Code: Checks out the repository’s code.
  • AWS Credentials: Configures AWS credentials using secrets stored in GitHub.

Step 4: Build and Push Docker Image

The workflow builds your application into a Docker image and pushes it to your ECR repository.

  • Build and Tag: Builds the Docker image and tags it with the current commit SHA.
  • Push to ECR: Pushes the image to your Amazon ECR repository.

Step 5: Render Secrets from SSM Parameter Store

The workflow retrieves secrets from AWS SSM Parameter Store and injects them into your ECS task definition.

  • Render Secrets: Uses a custom GitHub Action to pull secrets from SSM and replace placeholders in your task definition.

Step 6: Deploy the ECS Task Definition

Finally, the workflow updates your ECS service with the new task definition, deploying the latest version of your application.

  • Deploy: Deploys the updated task definition to ECS and waits for the service to stabilize.

Connection Summary

  1. AWS Credentials: Utilizes IAM credentials stored as GitHub secrets for secure access to AWS.
  2. Docker Image Management: Builds, tags, and pushes your application as a Docker image to Amazon ECR.
  3. Secrets Handling: Retrieves and injects sensitive data from SSM Parameter Store into your ECS tasks.
  4. Automated Deployment: Ensures your latest code is automatically deployed to your staging environment on ECS.

This workflow automates the process of deploying your application whenever new code is pushed, ensuring a seamless CI/CD pipeline with secure and efficient management of resources and secrets.

On this page