Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: DBTP-1507 Setup codepipeline notifications #314

Draft
wants to merge 95 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
3f34fbf
initial ecr import terraform
james-francis-MT Nov 6, 2024
a59d34b
wip: use mapping when creating ecr repos
james-francis-MT Nov 6, 2024
b997edd
Refactor importing ecr repo
JohnStainsby Nov 6, 2024
f57a2a3
Move for loop out of ecr resource; Add codebuild project and supporti…
JohnStainsby Nov 6, 2024
4c2f12f
Change config variable to pipelines and add codebase name; Add IAM po…
JohnStainsby Nov 7, 2024
d9646bd
Initial codebuild unit test
JohnStainsby Nov 7, 2024
135863f
Add correct image build buildspec
JohnStainsby Nov 7, 2024
83b3c17
Codebuild tests
JohnStainsby Nov 7, 2024
cecc294
Tests for webhook filters
JohnStainsby Nov 7, 2024
9bea12e
ECR tests
JohnStainsby Nov 7, 2024
c4a6d34
Iam policy tests
JohnStainsby Nov 7, 2024
bc6fd58
Permissions fixes
JohnStainsby Nov 7, 2024
1a1d75f
Merge branch 'main' into DBTP-1502-terraform-image-build-codebuild-pr…
JohnStainsby Nov 7, 2024
ec908ed
Fix checkov warnings
JohnStainsby Nov 7, 2024
fc44383
Remove unnecessary codebuild test
JohnStainsby Nov 7, 2024
8cfcfff
Remove commented line
JohnStainsby Nov 8, 2024
7ae1fd6
Initial pipeline config
JohnStainsby Nov 8, 2024
8d4d04b
Work out service deploy stage run order
JohnStainsby Nov 8, 2024
5e723ce
Add ECS deploy configuration
JohnStainsby Nov 8, 2024
43af432
Add codebuild project for creating deploy manifest files
JohnStainsby Nov 8, 2024
3f49bd2
Update buildspec for deploy manifests
JohnStainsby Nov 11, 2024
f177e2d
Pass variables to buildspec template file
JohnStainsby Nov 11, 2024
3e82991
Add source stage; Fix run order list sorting;
JohnStainsby Nov 11, 2024
23776cf
Add manifest codebuild permissions
JohnStainsby Nov 11, 2024
20b6dbf
Tighten ecs permissions
JohnStainsby Nov 12, 2024
e12e9a7
IAM permissions for ECS deploy
JohnStainsby Nov 12, 2024
9f5d0ae
Event bridge trigger for pipelines
JohnStainsby Nov 12, 2024
1028573
Add approval stages
JohnStainsby Nov 12, 2024
e1dc70c
Merge branch 'main' into DBTP-1503-terraform-codebase-pipeline
JohnStainsby Nov 12, 2024
b121509
Refactor codebuild jobs; Formatting
JohnStainsby Nov 12, 2024
33f23a2
Tighten ECS deploy permissions
JohnStainsby Nov 12, 2024
548a022
Test manifest codebuild; Deploy pipeline; Event bridge trigger
JohnStainsby Nov 13, 2024
9e913a0
Test run groups
JohnStainsby Nov 13, 2024
8a17010
Fix checkov comments
JohnStainsby Nov 13, 2024
e70227b
Add codebase pipeline deploy role to be deployed with each environmen…
JohnStainsby Nov 14, 2024
fe7ba12
Assume environment codebase pipeline deploy role; Add account config …
JohnStainsby Nov 14, 2024
3b103b0
Fix permssions
JohnStainsby Nov 14, 2024
99aab62
Update env deploy permissions
JohnStainsby Nov 14, 2024
716a41d
Assume env deploy role in pipeline and codebuild
JohnStainsby Nov 14, 2024
7f968c8
Allow deploy role artifact bucket access; Refactor permssions
JohnStainsby Nov 14, 2024
7e19263
Formatting
JohnStainsby Nov 14, 2024
3c7db1e
Unit tests for stage role_arn and extensions IAM role
JohnStainsby Nov 15, 2024
bae0d4d
Fix linting and validate error
JohnStainsby Nov 15, 2024
a4627be
Formatting
JohnStainsby Nov 15, 2024
9c5bea2
Only write one set of service deploy manifests
JohnStainsby Nov 15, 2024
34c6b76
fix bug in manifests buildspec
james-francis-MT Nov 15, 2024
ba34293
only use one codebuild for each codebase
james-francis-MT Nov 15, 2024
1b1a8c4
Fix manifest bug
JohnStainsby Nov 15, 2024
57a8251
Only output one deploy manifest per service
JohnStainsby Nov 15, 2024
be32434
Fix tests for codebuild project
JohnStainsby Nov 15, 2024
7fd8cb2
Allow service manifest codebuild to export variables for any environm…
JohnStainsby Nov 18, 2024
49c4693
Formatting
JohnStainsby Nov 18, 2024
6afb6c0
Merge branch 'main' into DBTP-1503-cross-account-ecs-deploy
JohnStainsby Nov 26, 2024
8a4b2f2
Fix IAM tests
JohnStainsby Nov 28, 2024
75caef1
Update extensions tests
JohnStainsby Nov 28, 2024
b403a05
Fix test error message
JohnStainsby Nov 28, 2024
0ad20ff
Merge branch 'main' into DBTP-1503-cross-account-ecs-deploy
JohnStainsby Dec 12, 2024
b87b8fe
Refactor environment IAM permissions
JohnStainsby Dec 16, 2024
2f23795
Role rename
JohnStainsby Dec 16, 2024
92e47e5
Refactor pipeline IAM permissions
JohnStainsby Dec 16, 2024
e2e5056
Remove unnecessary IAM permission; Fix deploy manifest buildspec
JohnStainsby Dec 16, 2024
0157bba
Merge branch 'main' into DBTP-1503-cross-account-ecs-deploy
JohnStainsby Dec 16, 2024
53a1dd9
Update test error
JohnStainsby Dec 16, 2024
b13341b
Merge branch 'DBTP-1503-cross-account-ecs-deploy' into DBTP-1503-manu…
JohnStainsby Dec 16, 2024
351394e
Fix existing tests, codebuild environment variables
JohnStainsby Dec 16, 2024
d12bf4f
Export extra variables for manual release pipeline
JohnStainsby Dec 16, 2024
d38bece
Change manual pipeline to use codebuild for deployment actions
JohnStainsby Dec 17, 2024
9889c0c
Add tests for manual release pipeline; Refactor policy documents into…
JohnStainsby Dec 17, 2024
8fb5d97
Use arnlike for IAM resource
JohnStainsby Dec 17, 2024
817ab24
Merge branch 'DBTP-1503-cross-account-ecs-deploy' into DBTP-1503-manu…
JohnStainsby Dec 17, 2024
69fb293
Change all deployments actions to use codebuild
JohnStainsby Dec 18, 2024
374630f
Standardise capitalisation
JohnStainsby Dec 18, 2024
fe2ba34
Refactor locals to remove unnecessary config from environment variable
JohnStainsby Dec 18, 2024
0d19cfa
Add checks for environment and image_tag pipeline variables
JohnStainsby Dec 19, 2024
4084d0f
Add custom python script block to set automatic stage rollback
JohnStainsby Dec 20, 2024
a7f63a3
Ensure the correct containers image is updated; Restore desired taskc…
JohnStainsby Dec 23, 2024
eea7e9d
Update tests for source stage change
JohnStainsby Dec 24, 2024
a79d19e
Provider update changes to elasticache at_rest_encryption_enabled pro…
JohnStainsby Dec 24, 2024
2fbcbc1
add buildspec-deploy.yml file for codebase module
tony-griffin Jan 15, 2025
d74a458
add install platform -helper
tony-griffin Jan 15, 2025
31f6ee1
Merge branch 'DBTP-1503-manual-pipeline' into DBTP-1507-setup-codepip…
tony-griffin Jan 15, 2025
065f434
Add codebuild env vars for deploy buildspec
tony-griffin Jan 15, 2025
7c43eb5
Add local env vars for use in deploy buildspec
tony-griffin Jan 15, 2025
8f0e3a4
Add platform-helper notify slack messages to codebase deploy buildspec
tony-griffin Jan 15, 2025
ad16c0f
Add slack channel module variable
tony-griffin Jan 15, 2025
1a1f80c
Add slack channel variable parameter store type
tony-griffin Jan 15, 2025
0b84d7c
Update tests to reflect new codebase codebuild env vars
tony-griffin Jan 16, 2025
04667f7
Add platform-helper notify command with emojis
tony-griffin Jan 16, 2025
6479d1f
Add IAM permissions for ECR & SSM access for deploy buildspec
tony-griffin Jan 16, 2025
5f1a862
Build env vars for pipeline execution ID URL
tony-griffin Jan 17, 2025
cd25d02
Add default codepipeline execution ID env var at pipeline level
tony-griffin Jan 17, 2025
5c7cddf
Update terraform tests to reflect adding env vars to codebuild actions
tony-griffin Jan 17, 2025
069b091
Add extra codebuild env vars to manual release codebase pipeline
tony-griffin Jan 18, 2025
3e61b5d
FIX: environment variable for manual release pipeline
tony-griffin Jan 18, 2025
26d8ebf
Update tests for manual release pipeline env vars
tony-griffin Jan 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions codebase-pipelines/artifactstore.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,10 @@ data "aws_iam_policy_document" "artifact_store_bucket_policy" {
type = "*"
identifiers = ["*"]
}

actions = [
"s3:*",
"s3:*"
]

effect = "Deny"

condition {
test = "Bool"
variable = "aws:SecureTransport"
Expand All @@ -46,7 +43,24 @@ data "aws_iam_policy_document" "artifact_store_bucket_policy" {
"false",
]
}
resources = [
aws_s3_bucket.artifact_store.arn,
"${aws_s3_bucket.artifact_store.arn}/*",
]
}

statement {
effect = "Allow"
principals {
type = "AWS"
identifiers = [
for env in local.pipeline_environments :
"arn:aws:iam::${env.account}:role/${var.application}-${env.name}-codebase-pipeline-deploy"
]
}
actions = [
"s3:*"
]
resources = [
aws_s3_bucket.artifact_store.arn,
"${aws_s3_bucket.artifact_store.arn}/*",
Expand All @@ -70,7 +84,7 @@ resource "aws_kms_key" "artifact_store_kms_key" {
"Sid" : "Enable IAM User Permissions",
"Effect" : "Allow",
"Principal" : {
"AWS" : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
"AWS" : [for id in local.deploy_account_ids : "arn:aws:iam::${id}:root"]
},
"Action" : "kms:*",
"Resource" : "*"
Expand Down
179 changes: 179 additions & 0 deletions codebase-pipelines/buildspec-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
version: 0.2

env:
variables:
DEPLOY_TIMEOUT: 1800
parameter-store:
SLACK_TOKEN: /codebuild/slack_oauth_token

phases:
install:
commands:
- echo "Starting deployment script for ${SERVICE} service"
- pip install yq --quiet
- echo "Installing platform-helper"
- pip install dbt-platform-helper --quiet
- platform-helper --version
- echo "Installing regclient"
- curl -s -L https://github.com/regclient/regclient/releases/latest/download/regctl-linux-amd64 > /usr/local/bin/regctl
- chmod +x /usr/local/bin/regctl

build:
commands:
- set -e

# Extract timestamp from image config and check if it exists
- aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

- |
SLACK_REF=$(regctl image config "${REPOSITORY_URL}:${IMAGE_TAG}" | jq -r '.config.Labels."uk.gov.trade.digital.build.timestamp"')
if [ "$SLACK_REF" = "null" ] || [ -z "$SLACK_REF" ]; then
echo "Image contains no timestamp label"
exit 1
fi
echo "Found image timestamp $SLACK_REF"

- UPPERCASE_SERVICE=$(echo "${SERVICE}" | tr '[:lower:]' '[:upper:]')
- UPPERCASE_TAG=$(echo "${IMAGE_TAG}" | tr '[:lower:]' '[:upper:]')

# Extract the pipeline name from CODEBUILD_INITIATOR default env var
- |
PIPELINE_NAME=""

if [[ "${CODEBUILD_INITIATOR}" == codepipeline/* ]]; then
PIPELINE_NAME=$(echo "${CODEBUILD_INITIATOR}" | cut -d'/' -f2)
echo "Pipeline name is ${PIPELINE_NAME}"
else
echo "ERROR: Build not triggered by CodePipeline."
exit 1
fi

- |
BUILD_ID_PREFIX=$(echo $CODEBUILD_BUILD_ID | cut -d':' -f1)
echo "BUILD_ID_PREFIX - ${BUILD_ID_PREFIX}"

- |
MESSAGE=":rocket: STARTED - Deployment for ${UPPERCASE_SERVICE} service - Build log <https://${AWS_REGION}.console.aws.amazon.com/codesuite/codebuild/${AWS_ACCOUNT_ID}/projects/${BUILD_ID_PREFIX}/build/${CODEBUILD_BUILD_ID}/?region=${AWS_REGION}|${CODEBUILD_BUILD_NUMBER}>"

- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}"

# Check if the specified image tag exists
- |
if ! aws ecr describe-images --repository-name "${REPOSITORY_NAME}" --image-ids "imageTag=${IMAGE_TAG}" > /dev/null 2>&1; then
echo "Error: image tag ${IMAGE_TAG} not found in repository ${REPOSITORY_NAME}"
exit 1
fi

# Check environment exists in config
- |
if [ $(echo $ENV_CONFIG | jq 'has('\"${ENVIRONMENT}\"')') == "false" ]; then
echo "Error: environment ${ENVIRONMENT} not listed in environment config"
exit 1
fi

- task_family="${APPLICATION}-${ENVIRONMENT}-${SERVICE}"
- cluster="${APPLICATION}-${ENVIRONMENT}"
- image_uri="${REPOSITORY_URL}:${IMAGE_TAG}"

# Assume environment role
- account_id=$(echo ${ENV_CONFIG} | jq -c -r .${ENVIRONMENT}.account)
- assumed_role=$(aws sts assume-role --role-arn "arn:aws:iam::${account_id}:role/${APPLICATION}-${ENVIRONMENT}-codebase-pipeline-deploy" --role-session-name "${ENVIRONMENT}-codebase-pipeline-deploy")
- export AWS_ACCESS_KEY_ID=$(echo $assumed_role | jq -r .Credentials.AccessKeyId)
- export AWS_SECRET_ACCESS_KEY=$(echo $assumed_role | jq -r .Credentials.SecretAccessKey)
- export AWS_SESSION_TOKEN=$(echo $assumed_role | jq -r .Credentials.SessionToken)

# Get service name
- service_name=$(aws ecs list-services --cluster "${cluster}" | jq -r '.serviceArns[] | select(contains("'${cluster}'-'${SERVICE}'-Service"))' | cut -d '/' -f3)

# Update task definition
- task_definition=$(aws ecs describe-task-definition --task-definition "${task_family}")
- new_task_definition=$(echo ${task_definition} | jq '.taskDefinition | .containerDefinitions |= map(if .name == '\"${SERVICE}\"' then .image = '\"${image_uri}\"' else . end) | del(.taskDefinitionArn) | del(.revision) | del(.status) | del(.requiresAttributes) | del(.compatibilities) | del(.registeredAt) | del(.registeredBy)')
- new_task_info=$(aws ecs register-task-definition --cli-input-json "${new_task_definition}")
- new_revision=$(echo ${new_task_info} | jq '.taskDefinition.revision')

# Get desired task count
- count=0
- cd "${CODEBUILD_SRC_DIR}"

# If count is a range, get the first value otherwise get count
- |
if [ $(yq '.count | type == "object"' copilot/web/manifest.yml) == "true" ]; then
count=$(yq '.count.range' copilot/web/manifest.yml | tr -d '"' | cut -d '-' -f1)
else
count=$(yq '.count' copilot/web/manifest.yml)
fi

# Check for environment overrides
- |
if [ $(yq '.environments.'${ENVIRONMENT}'.count | type == "object"' copilot/web/manifest.yml) == "true" ]; then
env_count=$(yq '.environments.'${ENVIRONMENT}'.count.range' copilot/web/manifest.yml | tr -d '"' | cut -d '-' -f1)
else
env_count=$(yq '.environments.'${ENVIRONMENT}'.count' copilot/web/manifest.yml)
fi

- |
if [[ "${env_count}" != "null" ]]; then
count=${env_count}
fi

# Start deployment
- echo "Deploying ${image_uri} to ${service_name} in ${cluster} with task count ${count}"
- start=$( date +%s )
- deploy_status="IN_PROGRESS"
- aws ecs update-service --cluster "${cluster}" --service "${service_name}" --task-definition "${task_family}:${new_revision}" --desired-count ${count} > /dev/null 2>&1

# Check deployment status
- |
while [[ "${deploy_status}" == "IN_PROGRESS" || "${deploy_status}" == "PENDING" || "${deploy_status}" == "ROLLBACK_IN_PROGRESS" ]];
do
sleep 10
now=$( date +%s )
elapsed=$(( now-start ))

deploy_status=$(aws ecs list-service-deployments --cluster "${cluster}" --service "${service_name}" --created-at "after=${start}" | jq -r '.serviceDeployments[0].status')
echo "Deployment status after ${elapsed} seconds: ${deploy_status}"

if [[ ${elapsed} -gt ${DEPLOY_TIMEOUT} ]]; then
echo "Error: deployment not completed in ${DEPLOY_TIMEOUT} seconds"
exit 1
fi
done

# Check deployment success
- |
case "${deploy_status}" in
SUCCESSFUL)
STATUS=":large_green_circle:"
;;
ROLLBACK_SUCCESSFUL)
STATUS=":large_orange_circle::white_check_mark:"
;;
ROLLBACK_IN_PROGRESS)
STATUS=":large_orange_circle::hourglass:"
;;
ROLLBACK_FAILED)
STATUS=":large_orange_circle::arrow_right::red_circle:"
;;
*)
STATUS=":large_blue_circle:"
;;
esac
echo "Status emoji set to ${STATUS}"

# Construct the pipeline execution URL
- |
PIPELINE_EXECUTION_URL="https://${AWS_REGION}.console.aws.amazon.com/codesuite/codepipeline/pipelines/${PIPELINE_NAME}/executions/${PIPELINE_EXECUTION_ID}"

echo "Pipeline execution URL: ${PIPELINE_EXECUTION_URL}"

- |
MESSAGE="${STATUS} COMPLETE - Deployment of ${UPPERCASE_TAG} to ${UPPERCASE_SERVICE} service - ${deploy_status} - <${PIPELINE_EXECUTION_URL}|Pipeline execution url>"

- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}"

# Exit with an error code if deployment did not succeed
- |
if [ "${deploy_status}" != "SUCCESSFUL" ]; then
echo "Error: deployment status is ${deploy_status}"
exit 1
fi
33 changes: 0 additions & 33 deletions codebase-pipelines/buildspec-manifests.yml

This file was deleted.

64 changes: 20 additions & 44 deletions codebase-pipelines/codebuild.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ data "aws_codestarconnections_connection" "github_codestar_connection" {
}

resource "aws_codebuild_project" "codebase_image_build" {
name = "${var.application}-${var.codebase}-codebase-image-build"
name = "${var.application}-${var.codebase}-codebase-pipeline-image-build"
description = "Publish images on push to ${var.repository}"
build_timeout = 30
service_role = aws_iam_role.codebase_image_build.arn
Expand Down Expand Up @@ -115,12 +115,11 @@ resource "aws_codebuild_webhook" "codebuild_webhook" {
}


resource "aws_codebuild_project" "codebase_deploy_manifests" {
for_each = local.pipeline_map
name = "${var.application}-${var.codebase}-${each.value.name}-codebase-deploy-manifests"
description = "Create image deploy manifests to deploy services"
build_timeout = 5
service_role = aws_iam_role.codebuild_manifests.arn
resource "aws_codebuild_project" "codebase_deploy" {
name = "${var.application}-${var.codebase}-codebase-pipeline-deploy"
description = "Deploy specified image tag to specified environment"
build_timeout = 30
service_role = aws_iam_role.codebase_deploy.arn
encryption_key = aws_kms_key.artifact_store_kms_key.arn

artifacts {
Expand All @@ -137,59 +136,36 @@ resource "aws_codebuild_project" "codebase_deploy_manifests" {
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
type = "LINUX_CONTAINER"
image_pull_credentials_type = "CODEBUILD"

environment_variable {
name = "ENV_CONFIG"
value = jsonencode(local.base_env_config)
}
}

logs_config {
cloudwatch_logs {
group_name = aws_cloudwatch_log_group.codebase_deploy_manifests.name
stream_name = aws_cloudwatch_log_stream.codebase_deploy_manifests.name
group_name = aws_cloudwatch_log_group.codebase_deploy.name
stream_name = aws_cloudwatch_log_stream.codebase_deploy.name
}
}

source {
type = "CODEPIPELINE"
buildspec = templatefile("${path.module}/buildspec-manifests.yml", {
application = var.application,
environments = [
for env in each.value.environments : upper(env.name)
],
services = local.service_export_names
})
type = "CODEPIPELINE"
buildspec = file("${path.module}/buildspec-deploy.yml")
}

tags = local.tags
}

resource "aws_kms_key" "codebuild_kms_key" {
description = "KMS Key for ${var.application} ${var.codebase} CodeBuild encryption"
enable_key_rotation = true

policy = jsonencode({
Statement = [
{
"Sid" : "Enable IAM User Permissions",
"Effect" : "Allow",
"Principal" : {
"AWS" : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
},
"Action" : "kms:*",
"Resource" : "*"
}
]
Version = "2012-10-17"
})

tags = local.tags
}

resource "aws_cloudwatch_log_group" "codebase_deploy_manifests" {
resource "aws_cloudwatch_log_group" "codebase_deploy" {
# checkov:skip=CKV_AWS_338:Retains logs for 3 months instead of 1 year
# checkov:skip=CKV_AWS_158:Log groups encrypted using default encryption key instead of KMS CMK
name = "codebuild/${var.application}-${var.codebase}-codebase-deploy-manifests/log-group"
name = "codebuild/${var.application}-${var.codebase}-codebase-deploy/log-group"
retention_in_days = 90
}

resource "aws_cloudwatch_log_stream" "codebase_deploy_manifests" {
name = "codebuild/${var.application}-${var.codebase}-codebase-deploy-manifests/log-stream"
log_group_name = aws_cloudwatch_log_group.codebase_deploy_manifests.name
resource "aws_cloudwatch_log_stream" "codebase_deploy" {
name = "codebuild/${var.application}-${var.codebase}-codebase-deploy/log-stream"
log_group_name = aws_cloudwatch_log_group.codebase_deploy.name
}
Loading
Loading