Skip to content

Instantly share code, notes, and snippets.

@sashatu
Created June 15, 2023 17:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sashatu/0e1241443583bc9018de4cce6cc626f3 to your computer and use it in GitHub Desktop.
Save sashatu/0e1241443583bc9018de4cce6cc626f3 to your computer and use it in GitHub Desktop.
CloudFormation Template to deploy a CI/CD Pipeline for Terraform on AWS
AWSTemplateFormatVersion: 2010-09-09
Description: Create an AWS-native IaC Pipeline for Terraform deployments.
Make sure that the Stack name is distinct across all AWS Regions for this account.
Parameters:
ExistingGitHubConnection:
Type: String
Description: Provide an existing GitHub Connection ARN or leave blank to create a new one
GitHubRepo:
Type: String
Description: Name of GitHub Repo to be used by the pipeline (user_name/repo_name)
GitHubBranch:
Type: String
Description: Name of GitHub Branch to be used by the pipeline
Default: main
BuildImageName:
Type: String
Description: Docker image for build projects - Ubuntu is recommended
Default: aws/codebuild/standard:7.0
TerraformVersion:
Type: String
Description: Version of Terraform to be used by the pipeline
Default: 1.4.6
Conditions:
NewGitHubConnection: !Equals
- ""
- !Ref ExistingGitHubConnection
Resources:
GitHubConnection:
Type: 'AWS::CodeStarConnections::Connection'
Properties:
ConnectionName: !If [NewGitHubConnection, !Sub "${AWS::StackName}-${AWS::AccountId}-github", !Sub "${AWS::StackName}-to_be_deleted"]
ProviderType: GitHub
PipelineBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-codepipeline
TerraformStateBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-tfstate
TerraformLockTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub ${AWS::StackName}-${AWS::AccountId}-tflock
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
-
AttributeName: "LockID"
AttributeType: "S"
KeySchema:
-
AttributeName: "LockID"
KeyType: "HASH"
PipelineServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${AWS::StackName}-pipeline-service-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- codepipeline.amazonaws.com
- codebuild.amazonaws.com
Policies:
- PolicyName: !Sub ${AWS::StackName}-CodePipelineInlinePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: UseGitHubConnection
Resource: '*'
Effect: Allow
Action:
- codestar-connections:UseConnection
- Sid: CodeBuildPermissions
Resource: '*'
Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- sns:Publish
- Sid: CloudWatchLogs
Resource: '*'
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- Sid: AccessPipelineBucket
Effect: Allow
Action:
- s3:Get*
- s3:ListBucket
Resource:
- !Sub arn:aws:s3:::${PipelineBucket}
- Sid: AccessPipelineBucketObjects
Effect: Allow
Action:
- s3:PutObject*
- s3:GetObject*
Resource:
- !Sub arn:aws:s3:::${PipelineBucket}/*
ProjectServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${AWS::StackName}-codebuild-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- codebuild.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
TerraformPlanProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${AWS::StackName}-terraform-plan
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Type: LINUX_CONTAINER
Image: !Ref BuildImageName
ServiceRole: !GetAtt ProjectServiceRole.Arn
LogsConfig:
CloudWatchLogs:
Status: ENABLED
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
env:
exported-variables:
- BuildID
- BuildTag
phases:
install:
commands:
- "curl -s https://releases.hashicorp.com/terraform/${TerraformVersion}/terraform_${TerraformVersion}_linux_amd64.zip -o terraform.zip"
- "unzip terraform.zip -d /usr/local/bin"
- "chmod 755 /usr/local/bin/terraform"
- "rm terraform.zip"
pre_build:
commands:
- terraform init -input=false -backend-config="bucket=${TerraformStateBucket}" -backend-config="key=${AWS::StackName}.tfstate" -backend-config="dynamodb_table=${TerraformLockTable}" -backend-config="region=${AWS::Region}"
build:
commands:
- terraform plan -lock=true -input=false -out=${AWS::StackName}-terraform.tfplan -no-color
artifacts:
name: TerraformPlan
files:
- ${AWS::StackName}-terraform.tfplan
TerraformApplyProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${AWS::StackName}-terraform-apply
Artifacts:
Type: CODEPIPELINE
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Type: LINUX_CONTAINER
Image: !Ref BuildImageName
ServiceRole: !GetAtt ProjectServiceRole.Arn
LogsConfig:
CloudWatchLogs:
Status: ENABLED
Source:
Type: CODEPIPELINE
BuildSpec: !Sub |
version: 0.2
env:
exported-variables:
- BuildID
- BuildTag
phases:
install:
commands:
- "curl -s https://releases.hashicorp.com/terraform/${TerraformVersion}/terraform_${TerraformVersion}_linux_amd64.zip -o terraform.zip"
- "unzip terraform.zip -d /usr/local/bin"
- "chmod 755 /usr/local/bin/terraform"
- "rm terraform.zip"
pre_build:
commands:
- terraform init -input=false -backend-config="bucket=${TerraformStateBucket}" -backend-config="key=${AWS::StackName}.tfstate" -backend-config="dynamodb_table=${TerraformLockTable}" -backend-config="region=${AWS::Region}"
build:
commands:
- cp $CODEBUILD_SRC_DIR_TerraformPlan/${AWS::StackName}-terraform.tfplan .
- terraform apply ${AWS::StackName}-terraform.tfplan
IaCPipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt PipelineServiceRole.Arn
Name: !Sub ${AWS::StackName}-pipeline
ArtifactStores:
- Region: !Ref AWS::Region
ArtifactStore:
Type: S3
Location: !Sub ${PipelineBucket}
Stages:
- Name: Get-GitHub-Source
Actions:
- Name: GitHub
RunOrder: 1
ActionTypeId:
Category: Source
Provider: CodeStarSourceConnection
Owner: AWS
Version: '1'
Namespace: GitHubSource
OutputArtifacts:
- Name: GitHubCode
Configuration:
ConnectionArn: !If [NewGitHubConnection, !Ref GitHubConnection, !Ref ExistingGitHubConnection]
FullRepositoryId: !Ref GitHubRepo
BranchName: !Ref GitHubBranch
OutputArtifactFormat: CODE_ZIP
DetectChanges: true
- Name: Create-Terraform-Plan
Actions:
- Name: terraform_plan
RunOrder: 1
Namespace: TFPlan
InputArtifacts:
- Name: GitHubCode
OutputArtifacts:
- Name: TerraformPlan
ActionTypeId:
Category: Build
Provider: CodeBuild
Owner: AWS
Version: '1'
Configuration:
ProjectName: !Ref TerraformPlanProject
- Name: Approve-Terraform-Plan
Actions:
- Name: review-plan
RunOrder: 1
ActionTypeId:
Category: Approval
Provider: Manual
Owner: AWS
Version: '1'
- Name: Apply-Terraform-Plan
Actions:
- Name: terraform-apply
RunOrder: 1
Namespace: TFApply
InputArtifacts:
- Name: GitHubCode
- Name: TerraformPlan
ActionTypeId:
Category: Build
Provider: CodeBuild
Owner: AWS
Version: '1'
Configuration:
ProjectName: !Ref TerraformApplyProject
PrimarySource: GitHubCode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment