Skip to content

Commit

Permalink
feat: DBTP-1137 trigger prod pipeline from non-prod pipeline (#195)
Browse files Browse the repository at this point in the history
Co-authored-by: Chiara Mapelli <[email protected]>
Co-authored-by: Chiara Mapelli <[email protected]>
Co-authored-by: Will Gibson <[email protected]>
  • Loading branch information
4 people authored Aug 8, 2024
1 parent afb1829 commit d350039
Show file tree
Hide file tree
Showing 13 changed files with 609 additions and 92 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
*.envrc
*.idea*
*.DS_Store
*.env*
*venv*
13 changes: 7 additions & 6 deletions environment-pipelines/buildspec-apply.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@ phases:
- echo -e "\nWorking on environment ${ENVIRONMENT}"
- cd "terraform/environments/${ENVIRONMENT}"
- terraform apply plan.tfplan
- echo -e "\nGenerating manifests and deploying AWS Copilot environment resources"
- cd "${CODEBUILD_SRC_DIR}"
post_build:
commands:
- |
if [ "${CODEBUILD_BUILD_SUCCEEDING}" == "1" ]
then
MESSAGE="Terraform apply phase complete"
MESSAGE="Terraform apply phase complete for the ${ENVIRONMENT} environment."
ADDITIONAL_OPTIONS=""
else
MESSAGE="Terraform apply phase FAILED"
MESSAGE=":alert: Terraform apply phase FAILED for the ${ENVIRONMENT} environment."
ADDITIONAL_OPTIONS="--send-to-main-channel true"
fi
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE} for the ${ENVIRONMENT} environment."
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}" ${ADDITIONAL_OPTIONS}
artifacts:
files: []
files:
- "**/*"
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ phases:
- export "PATH=$(pwd)/bin:$PATH"
- export PYTHONPATH=$(pwd)
- |
export SLACK_REF=$(platform-helper notify environment-progress "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" \
"*Deploying ${APPLICATION} environments*" \
--build-arn "${CODEBUILD_BUILD_ARN}" \
--repository "${REPOSITORY}" \
--commit-sha "${CODEBUILD_RESOLVED_SOURCE_VERSION: -7}")
if [ "${SLACK_THREAD_ID}" == "NONE" ]
then
export SLACK_REF=$(platform-helper notify environment-progress "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" \
"*Pipeline ${PIPELINE_NAME}* is deploying ${APPLICATION} environments" \
--build-arn "${CODEBUILD_BUILD_ARN}" \
--repository "${REPOSITORY}" \
--commit-sha "${CODEBUILD_RESOLVED_SOURCE_VERSION: -7}")
else
export SLACK_REF="${SLACK_THREAD_ID}"
fi
- echo "Build SLACK_REF is - ${SLACK_REF}"
- cd bin
- curl -s -qL -o terraform_install.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
- unzip terraform_install.zip
Expand Down
19 changes: 10 additions & 9 deletions environment-pipelines/buildspec-plan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ phases:
commands:
- set -e
- echo "Cancelling any pending approvals for ${APPLICATION}-${ENVIRONMENT}-environment-pipeline"
- PIPELINE_STATE=$(aws codepipeline get-pipeline-state --name "${APPLICATION}-${ENVIRONMENT}-environment-pipeline")
- PIPELINE_STATE=$(aws codepipeline get-pipeline-state --name "${APPLICATION}-${PIPELINE_NAME}-environment-pipeline")
- PIPELINE_APPROVAL_EXECID=$(echo $PIPELINE_STATE | jq --arg stage "Approve-${ENVIRONMENT}" -r '.stageStates[] | select(.stageName == $stage and .latestExecution.status == "InProgress") | .latestExecution.pipelineExecutionId')
- |
if [ -n "${PIPELINE_APPROVAL_EXECID}" ]; then
aws codepipeline stop-pipeline-execution --pipeline-name "${APPLICATION}-${ENVIRONMENT}-environment-pipeline" --pipeline-execution-id $PIPELINE_APPROVAL_EXECID --abandon --reason "Abandoning previous pipeline execution pending approval to run terraform plan"
aws codepipeline stop-pipeline-execution --pipeline-name "${APPLICATION}-${PIPELINE_NAME}-environment-pipeline" --pipeline-execution-id $PIPELINE_APPROVAL_EXECID --abandon --reason "Abandoning previous pipeline execution pending approval to run terraform plan"
fi
- echo "Terraform Plan Phase"
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "Starting terraform plan phase for the ${ENVIRONMENT} environment."
Expand All @@ -37,16 +37,17 @@ phases:
commands:
- export BUILD_ID="$CODEBUILD_BUILD_ID"
- |
if [ "${CODEBUILD_BUILD_SUCCEEDING}" == "1" ]
then
platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "Terraform plan phase complete for the ${ENVIRONMENT} environment."
if [ "${NEEDS_APPROVAL}" == "yes" ]
then
platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "Waiting for approval for the ${ENVIRONMENT} environment."
if [ "${CODEBUILD_BUILD_SUCCEEDING}" == "1" ]; then
MESSAGE="Terraform plan phase complete for the ${ENVIRONMENT} environment."
if [ "${NEEDS_APPROVAL}" == "yes" ]; then
MESSAGE="${MESSAGE} Waiting for approval for the ${ENVIRONMENT} environment."
fi
ADDITIONAL_OPTIONS=""
else
platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "Terraform plan phase FAILED for the ${ENVIRONMENT} environment."
MESSAGE=":alert: Terraform plan phase FAILED for the ${ENVIRONMENT} environment."
ADDITIONAL_OPTIONS="--send-to-main-channel true"
fi
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}" "${ADDITIONAL_OPTIONS}"
artifacts:
files:
- "**/*"
52 changes: 52 additions & 0 deletions environment-pipelines/buildspec-trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
version: 0.2

env:
parameter-store:
SLACK_TOKEN: /codebuild/slack_oauth_token

phases:
install:
commands:
- export PATH="${CODEBUILD_SRC_DIR}/build-tools/bin:$PATH"
- export PYTHONPATH="${CODEBUILD_SRC_DIR}/build-tools"
- echo -e "\nAssume triggered account role to trigger ${TRIGGERED_PIPELINE_NAME} pipeline"

- assumed_role=$(aws sts assume-role --role-arn "${TRIGGERED_ACCOUNT_ROLE_ARN}" --role-session-name "trigger-prod-pipeline-$(date +%s)")

- PROD_AWS_ACCESS_KEY_ID=$(echo $assumed_role | jq -r .Credentials.AccessKeyId)
- PROD_AWS_SECRET_ACCESS_KEY=$(echo $assumed_role | jq -r .Credentials.SecretAccessKey)
- PROD_AWS_SESSION_TOKEN=$(echo $assumed_role | jq -r .Credentials.SessionToken)

- export PROFILE_NAME="${TRIGGERED_PIPELINE_AWS_PROFILE}"
# Populate the ~/.aws/credentials file..
- aws configure set aws_access_key_id "${PROD_AWS_ACCESS_KEY_ID}" --profile "${PROFILE_NAME}"
- aws configure set aws_secret_access_key "${PROD_AWS_SECRET_ACCESS_KEY}" --profile "${PROFILE_NAME}"
- aws configure set aws_session_token "${PROD_AWS_SESSION_TOKEN}" --profile "${PROFILE_NAME}"
# Populate the ~/.aws/config file..
- aws configure set region "eu-west-2" --profile "${PROFILE_NAME}"
- aws configure set output "json" --profile "${PROFILE_NAME}"

build:
commands:
- set -e
- MESSAGE="Triggering ${TRIGGERED_PIPELINE_NAME} pipeline"
- echo "${MESSAGE}"
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE}"

- aws codepipeline start-pipeline-execution --name "${TRIGGERED_PIPELINE_NAME}" --profile "${PROFILE_NAME}" --variables "name=SLACK_THREAD_ID,value=${SLACK_REF}"

post_build:
commands:
- |
if [ "${CODEBUILD_BUILD_SUCCEEDING}" == "1" ]
then
MESSAGE="SUCCESSFULLY triggered ${TRIGGERED_PIPELINE_NAME}"
ADDITIONAL_OPTIONS=""
else
MESSAGE=":alert: @here FAILED to trigger ${TRIGGERED_PIPELINE_NAME}"
ADDITIONAL_OPTIONS="--send-to-main-channel true"
fi
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "${MESSAGE} in the ${ACCOUNT_NAME} account." "${ADDITIONAL_OPTIONS}"

artifacts:
files: []
41 changes: 40 additions & 1 deletion environment-pipelines/codebuild.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ resource "aws_codebuild_project" "environment_pipeline_build" {

source {
type = "CODEPIPELINE"
buildspec = file("${path.module}/buildspec.yml")
buildspec = file("${path.module}/buildspec-install-build-tools.yml")
}

tags = local.tags
Expand Down Expand Up @@ -124,3 +124,42 @@ resource "aws_codebuild_project" "environment_pipeline_apply" {

tags = local.tags
}

resource "aws_codebuild_project" "trigger_other_environment_pipeline" {
for_each = toset(local.triggers_another_pipeline ? [""] : [])
name = "${var.application}-${var.pipeline_name}-environment-pipeline-trigger"
description = "Triggers a target pipeline"
build_timeout = 5
service_role = aws_iam_role.environment_pipeline_codebuild.arn
encryption_key = module.artifact_store.kms_key_arn

artifacts {
type = "CODEPIPELINE"
}

cache {
type = "S3"
location = module.artifact_store.bucket_name
}

environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
type = "LINUX_CONTAINER"
image_pull_credentials_type = "CODEBUILD"
}

logs_config {
cloudwatch_logs {
group_name = aws_cloudwatch_log_group.environment_pipeline_codebuild.name
stream_name = aws_cloudwatch_log_stream.environment_pipeline_codebuild.name
}
}

source {
type = "CODEPIPELINE"
buildspec = file("${path.module}/buildspec-trigger.yml")
}

tags = local.tags
}
10 changes: 9 additions & 1 deletion environment-pipelines/codepipeline.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ resource "aws_codepipeline" "environment_pipeline" {
depends_on = [aws_iam_role_policy.artifact_store_access_for_environment_codebuild]
pipeline_type = "V2"

variable {
name = "SLACK_THREAD_ID"
default_value = "NONE"
description = "This can be set by a triggering pipeline to continue an existing message thread"
}

artifact_store {
location = module.artifact_store.bucket_name
type = "S3"
Expand Down Expand Up @@ -39,7 +45,7 @@ resource "aws_codepipeline" "environment_pipeline" {
}

stage {
name = "Build"
name = "Install-Build-Tools"

action {
name = "InstallTools"
Expand All @@ -56,8 +62,10 @@ resource "aws_codepipeline" "environment_pipeline" {
PrimarySource = "project_deployment_source"
EnvironmentVariables : jsonencode([
{ name : "APPLICATION", value : var.application },
{ name : "PIPELINE_NAME", value : var.pipeline_name },
{ name : "REPOSITORY", value : var.repository },
{ name : "SLACK_CHANNEL_ID", value : var.slack_channel, type : "PARAMETER_STORE" },
{ name : "SLACK_THREAD_ID", value : "#{variables.SLACK_THREAD_ID}" },
])
}
}
Expand Down
60 changes: 60 additions & 0 deletions environment-pipelines/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -907,3 +907,63 @@ resource "aws_iam_role_policy" "copilot_assume_role_for_environment_codebuild" {
role = aws_iam_role.environment_pipeline_codebuild.name
policy = data.aws_iam_policy_document.copilot_assume_role.json
}

########### TRIGGERED PIPELINE RESOURCES ##########

#------PROD-TARGET-ACCOUNT------
resource "aws_iam_role" "trigger_pipeline" {
for_each = local.set_of_triggering_pipeline_names
name = "${var.application}-${var.pipeline_name}-trigger-pipeline-from-${each.value}"
assume_role_policy = data.aws_iam_policy_document.assume_trigger_pipeline.json
tags = local.tags
}

data "aws_iam_policy_document" "assume_trigger_pipeline" {
statement {
effect = "Allow"
principals {
type = "AWS"
identifiers = local.triggering_pipeline_role_arns
}
actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role_policy" "trigger_pipeline" {
for_each = local.set_of_triggering_pipeline_names
name = "${var.application}-${var.pipeline_name}-trigger-pipeline-from-${each.value}"
role = aws_iam_role.trigger_pipeline[each.value].name
policy = data.aws_iam_policy_document.trigger_pipeline[each.value].json
}

data "aws_iam_policy_document" "trigger_pipeline" {
for_each = local.set_of_triggering_pipeline_names
statement {
actions = [
"codepipeline:StartPipelineExecution",
]
resources = [
aws_codepipeline.environment_pipeline.arn
]
}
}


#------NON-PROD-SOURCE-ACCOUNT------

resource "aws_iam_role_policy" "assume_role_to_trigger_pipeline_policy" {
for_each = toset(local.triggers_another_pipeline ? [""] : [])
name = "${var.application}-${var.pipeline_name}-assume-role-to-trigger-codepipeline-policy"
role = aws_iam_role.environment_pipeline_codebuild.name
policy = data.aws_iam_policy_document.assume_role_to_trigger_codepipeline_policy_document[""].json
}

data "aws_iam_policy_document" "assume_role_to_trigger_codepipeline_policy_document" {
for_each = toset(local.triggers_another_pipeline ? [""] : [])
statement {
actions = [
"sts:AssumeRole"
]
resources = [local.triggered_pipeline_account_role]
}
}
Loading

0 comments on commit d350039

Please sign in to comment.