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_iammodule 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_onensures 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_cimodule 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
- CI IAM User Creation: Sets up an IAM user with the required permissions for deploying your application.
- Policy Attachment: Links a custom IAM policy to the CI user, allowing it to interact with AWS resources securely.
- 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: trueWhat This Does:
- Triggers on Push: The workflow starts whenever there’s a push to the
mainbranch. - 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
- AWS Credentials: Utilizes IAM credentials stored as GitHub secrets for secure access to AWS.
- Docker Image Management: Builds, tags, and pushes your application as a Docker image to Amazon ECR.
- Secrets Handling: Retrieves and injects sensitive data from SSM Parameter Store into your ECS tasks.
- 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.