GitLab
GitLab
GITLAB CI/CD
PIPELINES
• S3 + DynamoDB backend is ideal for team environments where multiple users or CI/CD pipelines are working on the same
infrastructure.
• For single user or small projects, local state can work, but using S3 for shared state and DynamoDB for locking is highly
recommended for collaboration and stability.
Page | 1
BY VISHAL MACHAN
Step 1. Automating the Terraform Backend Setup (S3 + DynamoDB)
Steps in CodePipeline:
Conclusion:
By using CloudFormation, AWS CodePipeline, or GitHub Actions, you can fully automate the S3 + DynamoDB backend setup for
Terraform.
Page | 2
BY VISHAL MACHAN
Handling Deployment Rollbacks in GitLab CI/CD Pipelines
Rollback strategies in GitLab CI/CD depend on the deployment method (e.g., Kubernetes, Docker,
Terraform, or traditional virtual machines). Here’s how to implement rollbacks effectively:
Managing infrastructure and application code effectively requires version control, state
management, and rollback strategies. Below is a structured approach with detailed
scenarios and examples.
For infrastructure as code (IaC), tools like Terraform, CloudFormation, and AWS CDK are
used. Proper version control ensures collaboration, rollback, and auditing.
• Staging Branch: Used for testing infrastructure changes before merging to main.
• Feature Branches (feature-xyz): Used for adding new infrastructure features or modifications.
Feature Implementation
cidr_block = "10.0.0.0/16"
tags = {
Name = "MainVPC"
git add .
Testing in Staging
terraform init
terraform apply
Rollback Strategy
Page | 4
BY VISHAL MACHAN
o Deploy the previous version:
terraform apply
Since Terraform maintains a state file, concurrent deployments can cause conflicts. To prevent
this, state locking is used.
bucket = "my-terraform-state-bucket"
versioning {
enabled = true
lifecycle {
prevent_destroy = true
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
Page | 5
BY VISHAL MACHAN
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "infra/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
Developer A runs:
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "terraformstatestorage"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
output "vpc_id" { f
value = data.terraform_remote_state.network.outputs.vpc_id
}
class MyStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
_lambda.Function(
self, "MyLambdaFunction",
runtime=_lambda.Runtime.PYTHON_3_8,
handler="index.handler",
code=_lambda.Code.from_asset("lambda")
)
Deploy:
cdk deploy
Page | 7
BY VISHAL MACHAN
Check deployment history:
aws cloudformation describe-stacks --stack-name my-stack
E. Best Practices
Rolling back:
git checkout v1.0
git push origin v1.0:main
encrypt = true
Handling deployment rollbacks in GitLab CI/CD pipelines is essential for ensuring that, in case of
failure, you can revert to a previous stable state of application. Below are detailed strategies for
implementing and troubleshooting rollbacks for Docker, ECS, Kubernetes, and traditional virtual
machine deployments, with specific commands and steps.
o Tags in GitLab provide a clear reference point to specific versions of your application.
o A tag can be created in GitLab CI/CD after a successful build, before deployment.
Command to Create a Tag:
git tag v1.0
git push origin v1.0
B. Docker Rollback
If you're using Docker to deploy applications to AWS ECS or Kubernetes, you can store previous
Docker images in AWS ECR or GitLab’s Docker Registry and easily roll back by pulling a previous
image.
Page | 9
BY VISHAL MACHAN
• Each build pushed to the registry should have a version/tag (e.g., v1.0, v1.1).
2. Rollback Steps:
• Step 1: Check Existing Docker Images: In your pipeline, before deployment, confirm the images
that exist.
stages:
- build
- deploy
variables:
IMAGE_TAG: $CI_COMMIT_REF_NAME
build:
stage: build
script:
- docker build -t <aws_account_id>.dkr.ecr.<region>.amazonaws.com/my-app:$IMAGE_TAG .
- docker push <aws_account_id>.dkr.ecr.<region>.amazonaws.com/my-app:$IMAGE_TAG
deploy:
stage: deploy
script:
- aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment --image
<aws_account_id>.dkr.ecr.<region>.amazonaws.com/my-app:$IMAGE_TAG
Page | 10
BY VISHAL MACHAN
• Step 2: Rollback to a Specific Revision (e.g., v1.0):
kubectl rollout undo deployment/my-app --to-revision=1
If you use Helm to deploy your Kubernetes applications, you can rollback to a previous release
version.
f
• Step 1: Check Release History:
helm history my-app
If you are using Terraform to manage your ECS deployments, rollbacks can be done by simply
applying the previous configuration or modifying it to a previous state.
• Step 2: Modify or Revert the ECS Configuration to a Stable Version: we can update the task definition
or Docker image version in your Terraform code.
Page | 11
BY VISHAL MACHAN
A. Rollback Failures in Docker/ECR:
o Solution: Ensure the ECR repository exists and contains the correct image version. Use aws
ecr describe-images to verify the image.
o Command to Troubleshoot:
aws ecr describe-images --repository-name my-app
o Solution: Check ECS service logs and the Task Definition for any errors in the Docker image
configuration.
o Commands to Troubleshoot:
aws ecs describe-services --cluster my-cluster --services my-service
aws ecs describe-tasks --cluster my-cluster --tasks <task_id>
When implementing rollbacks in GitLab CI/CD pipelines, the following best practices can help
ensure smooth, automated, and reliable deployments. Here’s a detailed explanation of the five
best essential practices:
Page | 13
BY VISHAL MACHAN
How to Implement?
stages:
- build
- deploy
variables:
IMAGE_TAG: $CI_COMMIT_REF_NAME
deploy:
stage: deploy
only:
- tags # Ensures only tagged releases are deployed
script:
- docker pull my-registry/my-app:$IMAGE_TAG
- docker run -d -p 80:80 my-registry/my-app:$IMAGE_TAG
g
How to Implement?
Page | 14
BY VISHAL MACHAN
• Use GitLab’s after_script to detect failures.
• Implement health checks to trigger rollbacks.
• Monitor the deployment status and rollback automatically if necessary.
stages:
- build
- deploy
deploy:
stage: deploy
script:
- kubectl apply -f deployment.yaml
- kubectl rollout status deployment/my-app || kubectl rollout undo deployment/my-app
Why?
Monitoring deployment success or failure is essential for rollback automation and minimizing
downtime.
How to Implement?
deploy:
stage: deploy
script:
Page | 15
BY VISHAL MACHAN
- kubectl apply -f deployment.yaml
- sleep 30 # Wait for pods to start
- if ! curl -f http://my-app-service/health; then kubectl rollout undo deployment my-app; fi
groups:
- name: deployment_failure_alert
rules:
- alert: DeploymentFailed
expr: kube_deployment_status_replicas_unavailable > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Deployment Failure Detected"
description: "Deployment {{ $labels.deployment }} has unavailable replicas."
This rule triggers an alert if a deployment has unavailable replicas for more than 5 minutes.
d. Post-Deployment Validation
Why?
Deploying code without verifying it in a live environment can cause undetected issues. Automated
validation ensures new versions work correctly before finalizing deployment.
How to Implement?
validate:
stage: validate
script:
- echo "Running smoke tests..."
- curl -f http://my-app-service/health || exit 1
Page | 16
BY VISHAL MACHAN
e2e_tests:
stage: test
script:
- npx cypress run
How to Implement?
• Use unit tests, integration tests, and security scans before deployment.
• Implement an approval step in GitLab CI/CD for production releases.
• Require manual approval before rollback (if needed).
stages:
- test
- security_scan
- staging
- production
unit_tests:
stage: test
script:
- pytest tests/
security_scan:
stage: security_scan
script:
- trivy image my-app:latest
staging:
stage: staging v
script:
- kubectl apply -f staging-deployment.yaml
environment:
name: staging
when: manual # Requires manual trigger
Page | 17
BY VISHAL MACHAN
production:
stage: production
script:
- kubectl apply -f production-deployment.yaml
only:
- tags # Ensures only tagged releases are deployed
when: manual # Requires manual approval
Conclusion
By following these best practices, you ensure that your GitLab CI/CD pipeline is resilient,
automated, and rollback-friendly.
Version Control: Always use Git tags to deploy stable releases.
Automate Rollbacks: Automatically revert deployments upon failure.
Monitoring & Alerting: Use Prometheus, Grafana, or CloudWatch to detect issues.
Post-Deployment Validation: Run health checks, smoke tests, and end-to-end tests.
Quality Gates: Require testing, security scans, and manual approvals before production.
1.2.4 Since your deployment method involves Docker, ECS, Kubernetes, and
Terraform, here’s a tailored .gitlab-ci.yml template for different scenarios:
stages:
- build
- test
- security_scan
- deploy_staging
- deploy_production
variables:
IMAGE_TAG: $CI_COMMIT_REF_NAME
REGISTRY: my-registry/my-app
KUBE_NAMESPACE: my-namespace
DEPLOYMENT_NAME: my-app-deployment
Page | 18
BY VISHAL MACHAN
HEALTH_CHECK_URL: http://my-app-service/health
build:
stage: build
script:
- docker build -t $REGISTRY:$IMAGE_TAG .
- docker push $REGISTRY:$IMAGE_TAG
test:
stage: test
script:
- pytest tests/
security_scan:
stage: security_scan
script:
- trivy image $REGISTRY:$IMAGE_TAG
deploy_staging:
stage: deploy_staging
f
script:
- kubectl config set-context --current --namespace=$KUBE_NAMESPACE
- kubectl set image deployment/$DEPLOYMENT_NAME app=$REGISTRY:$IMAGE_TAG
- sleep 30
- if ! curl -f $HEALTH_CHECK_URL; then kubectl rollout undo deployment $DEPLOYMENT_NAME; fi
environment:
name: staging
when: manual
deploy_production:
stage: deploy_production
script:
- kubectl config set-context --current --namespace=$KUBE_NAMESPACE
- kubectl set image deployment/$DEPLOYMENT_NAME app=$REGISTRY:$IMAGE_TAG
- sleep 30
- if ! curl -f $HEALTH_CHECK_URL; then kubectl rollout undo deployment $DEPLOYMENT_NAME; fi
only:
- tags
when: manual
environment:
name: production
Features:
• Pushes a Docker image to the registry.
• Runs unit tests and security scans before deployment.
• Deploys to staging manually (requires approval).
Page | 19
BY VISHAL MACHAN
• Deploys to production only when a tag is pushed (e.g., v1.1).
• Automatic rollback if health check fails (curl -f validation).
stages:
- build
gggdbgdsh
- test
- deploy_staging
- deploy_production
variables:
AWS_REGION: us-east-1
ECR_REPO: my-ecr-repo
CLUSTER: my-ecs-cluster
SERVICE: my-ecs-service
IMAGE_TAG: $CI_COMMIT_REF_NAME
before_script:
- export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
build:
stage: build
script:
- docker build -t $ECR_REPO:$IMAGE_TAG .
- docker tag $ECR_REPO:$IMAGE_TAG
$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO:$IMAGE_TAG
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO:$IMAGE_TAG
test:
stage: test
script:
- pytest tests/
deploy_staging:
stage: deploy_staging
script:
- aws ecs update-service --cluster $CLUSTER --service $SERVICE --force-new-deployment
environment:
name: staging
when: manual
deploy_production:
stage: deploy_production
Page | 20
BY VISHAL MACHAN
script:
- aws ecs update-service --cluster $CLUSTER --service $SERVICE --force-new-deployment
only:
- tags
when: manual
environment:
name: production
Features:
stages:
- terraform_init
- terraform_plan
- terraform_apply
variables:
TF_WORKSPACE: production
AWS_REGION: us-east-1
terraform_init:
stage: terraform_init
script:
- terraform init
terraform_plan:
stage: terraform_plan
script:
- terraform plan -out=tfplan
when: manual
terraform_apply:
stage: terraform_apply
script:
- terraform apply tfplan
only:
- tags
ggggg Page | 21
BY VISHAL MACHAN
when: manual
Features:
1.2.5 GitLab CI/CD pipeline for deploying Terraform and AWS CDK infrastructure
securely.
stages:
- validate
- plan
- apply
variables:
TF_VERSION: "1.5.0"
AWS_REGION: "us-east-1"
S3_BUCKET: "my-terraform-state-bucket"
DYNAMODB_TABLE: "terraform-locks"
before_script:
Page | 22
BY VISHAL MACHAN
- apt-get update && apt-get install -y unzip
- curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add -
- echo "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee
/etc/apt/sources.list.d/hashicorp.list
- apt-get update && apt-get install -y terraform=$TF_VERSION
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "infra/terraform.tfstate"
Page | 23
BY VISHAL MACHAN
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
Pipeline Workflow
9. Create a merge request → GitLab runs terraform validate & terraform plan.
11. Merge into main branch → terraform apply is triggered (manual approval required).
This pipeline:
Installs AWS CDK
Synthesizes CloudFormation templates
Deploys CDK stacks to AWS
Supports rollback in case of failures
stages:
- build
- synth
- deploy
variables:
AWS_REGION: "us-east-1"
CDK_VERSION: "2.120.0"
Page | 24
BY VISHAL MACHAN
before_script:
# Install dependencies
install-dependencies:
stage: build
script:
- npm install
only:
- merge_requests
- main
cdk-synth:
stage: synth
script:
- cdk synth
only:
- merge_requests
cdk-deploy:
stage: deploy
script:
environment:
name: production
only:
Page | 25
BY VISHAL MACHAN
- main
runtime: lambda.Runtime.NODEJS_18_X,
handler: "index.handler",
code: lambda.Code.fromAsset("lambda"),
});
Pipeline Workflow
2. Create a merge request → GitLab runs npm install & cdk synth.
1.2.6 Conclusion:
With this GitLab CI/CD pipeline, you can automate infrastructure deployment while ensuring
validation, approvals, and rollback mechanisms.
This pipeline:
Page | 27
BY VISHAL MACHAN
Terraform Pipeline (buildspec.yml)
version: 0.2
env:
variables:
TF_VERSION: "1.5.0"
AWS_REGION: "us-east-1"
S3_BUCKET: "my-terraform-state-bucket"
DYNAMODB_TABLE: "terraform-locks"
phases:
install:
runtime-versions:
python: latest
pre_build:
commands:
- curl -O https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip
- unzip terraform_${TF_VERSION}_linux_amd64.zip
- mv terraform /usr/local/bin/
- terraform --version
build:
commands:
- terraform init
post_build:
Page | 28
BY VISHAL MACHAN
commands:
version: 0.2
phases:
install:
runtime-versions:
python: latest
build:
commands:
post_build:
commands:
Pipeline Workflow
This pipeline:
Page | 29
BY VISHAL MACHAN
Uses AWS Code Commit for CDK source.
Synthesizes CloudFormation templates.
Deploys CDK stacks to AWS.
Supports rollback in case of failure.
version: 0.2
phases:
install:
commands:
pre_build:
commands:
- npm install
build:
commands:
- cdk synth
Page | 30
BY VISHAL MACHAN
post_build:
commands:
version: 0.2
phases:
install:
commands:
build:
commands:
post_build:
commands:
Pipeline Workflow
Page | 31
BY VISHAL MACHAN
Uses AWS OIDC authentication (no credentials stored).
Supports manual approval before apply.
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
f
terraform:
runs-on: ubuntu-latest
steps:
uses: actions/checkout@v3
uses: hashicorp/setup-terraform@v2
with:
terraform_version: "1.5.0"
Page | 32
BY VISHAL MACHAN
- name: Terraform Init
uses: hmarr/auto-approve-action@v3
with:
on:
push:
Page | 33
BY VISHAL MACHAN
branches:
- main
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
f
- name: Checkout Code
uses: actions/checkout@v3
f
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: "18"
Page | 34
BY VISHAL MACHAN
Which One to Use?
Managed by AWS
S3 will store your Terraform state files, and DynamoDB will be used for state locking to prevent
concurrent modifications.
1. Go to AWS S3 Console and create a new bucket for storing Terraform state. For example, my-
terraform-state-bucket.
2. This DynamoDB table will help lock the state file to prevent concurrent Terraform runs from
affecting each other.
Page | 35
BY VISHAL MACHAN
You now need to modify your Terraform configuration to use the S3 backend for state storage and
DynamoDB for state locking.
backend "s3" {
region = "us-east-1" # AWS region where the S3 bucket and DynamoDB table exist
• bucket: The name of the S3 bucket you created for storing the state.
• key: Path to store the state file. Typically, this is terraform.tfstate or you can set it up with
subfolders (e.g., project_name/terraform.tfstate).
• region: The AWS region where your S3 bucket and DynamoDB table reside.
• Once your configuration is ready, run the following command in your terminal:
terraform init
Terraform will initialize the backend configuration and move your state to S3.
• Initial Setup: During the terraform init process, if this is your first time setting up the backend,
Terraform will ask you to confirm that it can migrate your local state to the new backend.
• State Migration: If you have an existing state file, Terraform will prompt to migrate your local state
to the S3 bucket.
Page | 36
BY VISHAL MACHAN
When Terraform is working with the state, it will create a lock in DynamoDB to ensure that only one
Terraform process can modify the state at any given time.
• When you run terraform apply or any other command that modifies the state, Terraform will
attempt to acquire a lock in DynamoDB.
• If another Terraform process is already running, the lock will be in place, and your process will
wait until it can acquire the lock.
• Once the backend is set up, any Terraform command (e.g., terraform plan, terraform apply,
terraform destroy) will interact with the remote backend. For example:
terraform plan
terraform apply
6. Advanced Considerations
• Versioning is essential for keeping multiple versions of your state files in case you need to roll
back changes.
• It can be enabled while creating the bucket or updated later through the S3 console.
• Encryption ensures the confidentiality of your state file, especially since it might contain sensitive
information (e.g., passwords, API keys).
• You can enable SSE-S3 (default encryption) or SSE-KMS (more control) to encrypt the state files
in S3.
Make sure the IAM role running Terraform (whether it’s via AWS CLI, CodePipeline, or GitHub Actions)
has the necessary permissions:
Page | 37
BY VISHAL MACHAN
"Version": "2012-10-17",
"Statement": [
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-terraform-state-bucket",
"arn:aws:s3:::my-terraform-state-bucket/*"
},
"Effect": "Allow",
"Action": "dynamodb:PutItem",
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/terraform-locks"
7. Final Notes
• S3 + DynamoDB backend is ideal for team environments where multiple users or CI/CD
pipelines are working on the same infrastructure.
• For single user or small projects, local state can work, but using S3 for shared state and
DynamoDB for locking is highly recommended for collaboration and stability.
• You can automate the creation of S3 and DynamoDB resources using AWS CloudFormation.
Here's the CloudFormation template:
AWSTemplateFormatVersion: "2010-09-09"
Resources:
TerraformStateBucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: "my-terraform-state-bucket"
VersioningConfiguration:
Status: "Enabled"
Tags:
- Key: "Name"
TerraformStateLockTable:
Type: "AWS::DynamoDB::Table"
Properties:
TableName: "terraform-locks"
AttributeDefinitions:
Page | 39
BY VISHAL MACHAN
- AttributeName: "LockID"
AttributeType: "S"
KeySchema:
- AttributeName: "LockID"
KeyType: "HASH"
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
Tags:
- Key: "Name"
You can deploy the CloudFormation stack using the AWS CLI:
Now that the backend resources are created, we will automate the deployment of Terraform code
through AWS CodePipeline or GitHub Actions.
In AWS CodePipeline, you can automate your Terraform deployment process, including initializing the
backend, planning, applying, and destroying resources.
Page | 40
BY VISHAL MACHAN
You will need:
• Source Stage: Use AWS CodeCommit or GitHub for storing your Terraform code.
• Build Stage: Use AWS CodeBuild to run Terraform commands like terraform init, terraform plan,
and terraform apply.
version: 0.2
phases:
install:
runtime-versions:
terraform: 1.x
commands:
pre_build:
commands:
build:
commands:
post_build:
commands:
artifacts:
Page | 41
BY VISHAL MACHAN
files:
- terraform.tfstate
Steps in CodePipeline:
on:
push:
branches:
- main
jobs:
terraform:
runs-on: ubuntu-latest
steps:
uses: actions/checkout@v2
Page | 42
BY VISHAL MACHAN
- name: Set up Terraform
uses: hashicorp/setup-terraform@v1
run: |
For CodePipeline, CodeBuild, or GitHub Actions to interact with AWS resources securely,
ensure they have the appropriate IAM roles and permissions.
Attach this policy to the service role in AWS CodeBuild or the IAM user used in GitHub Actions.
Page | 43
BY VISHAL MACHAN
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket",
"s3:PutBucketVersioning",
"s3:PutBucketEncryption"
],
"Resource": [
"arn:aws:s3:::my-terraform-state-bucket",
"arn:aws:s3:::my-terraform-state-bucket/*"
]
},
{
"Effect": "Allow",
"Action": "dynamodb:PutItem",
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/terraform-locks"
},
{
"Effect": "Allow",
"Action": "cloudformation:DescribeStacks",
"Resource": "*"
}
]
}
Conclusion:
By using CloudFormation, AWS CodePipeline, or GitHub Actions, you can fully automate the S3
+ DynamoDB backend setup for Terraform.
Page | 44
BY VISHAL MACHAN
• State locking is enabled via DynamoDB.
• Terraform actions (like apply) are automated in a CI/CD pipeline, reducing manual intervention.
Key Features:
Page | 45
BY VISHAL MACHAN
GitHub Actions Workflow (Terraform & CDK)
Key Features:
Page | 46
BY VISHAL MACHAN
Uses AWS OIDC (no stored credentials).
Supports manual approval before apply/deploy.
Comparison
THANK YOU
LINKEDIN: WWW.LINKEDIN.COM/IN/MACHAN-VISHAL
Page | 47
BY VISHAL MACHAN
MAIL [email protected]
CALL (217)5889385