Skip to content

Commit

Permalink
fix: DBTP-1338 - Run copilot env deploy in env pipelines (#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnStainsby authored Sep 13, 2024
1 parent d67710e commit ea3b56d
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 41 deletions.
21 changes: 21 additions & 0 deletions environment-pipelines/buildspec-apply.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,27 @@ 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}"
- platform-helper environment generate --name "${ENVIRONMENT}"
- |
if [[ "${AWS_PROFILE_FOR_COPILOT}" == *"prod"* ]]
then
echo -e "\nAssuming role to deploy AWS Copilot environment resources in prod account"
assumed_role=$(aws sts assume-role --role-arn "${TRIGGERING_ACCOUNT_CODEBUILD_ROLE}" --role-session-name "trigger-copilot-env-deploy-$(date +%s)")
NON_PROD_AWS_ACCESS_KEY_ID=$(echo $assumed_role | jq -r .Credentials.AccessKeyId)
NON_PROD_AWS_SECRET_ACCESS_KEY=$(echo $assumed_role | jq -r .Credentials.SecretAccessKey)
NON_PROD_AWS_SESSION_TOKEN=$(echo $assumed_role | jq -r .Credentials.SessionToken)
export PROFILE_NAME="${TRIGGERING_ACCOUNT_AWS_PROFILE}"
aws configure set aws_access_key_id "${NON_PROD_AWS_ACCESS_KEY_ID}" --profile "${PROFILE_NAME}"
aws configure set aws_secret_access_key "${NON_PROD_AWS_SECRET_ACCESS_KEY}" --profile "${PROFILE_NAME}"
aws configure set aws_session_token "${NON_PROD_AWS_SESSION_TOKEN}" --profile "${PROFILE_NAME}"
aws configure set region "eu-west-2" --profile "${PROFILE_NAME}"
aws configure set output "json" --profile "${PROFILE_NAME}"
export AWS_PROFILE="${PROFILE_NAME}"
fi
- copilot env init --name "${ENVIRONMENT}" --profile "${AWS_PROFILE_FOR_COPILOT}" --default-config
- copilot env deploy --name "${ENVIRONMENT}"
post_build:
commands:
- |
Expand Down
8 changes: 8 additions & 0 deletions environment-pipelines/buildspec-install-build-tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ phases:
commands:
- set -e
- echo "Deploying ${APPLICATION} environments"
- |
if [ ! -f .copilot-version ]; then
echo "Cannot find .copilot-version file"
exit 1
fi
- COPILOT_VERSION=`cat .copilot-version`
- cd "${CODEBUILD_SRC_DIR}"
- echo "Installing latest version of platform-helper to get required version for the project."
- pip install dbt-platform-helper
Expand Down Expand Up @@ -66,6 +72,8 @@ phases:
- unzip terraform_install.zip
- chmod +x terraform
- rm terraform_install.zip
- curl -s -qL -o copilot https://ecs-cli-v2-release.s3.amazonaws.com/copilot-linux-v${COPILOT_VERSION}
- chmod +x copilot
- platform-helper notify add-comment "${SLACK_CHANNEL_ID}" "${SLACK_TOKEN}" "${SLACK_REF}" "Starting install phase"
- VERSION_OUTPUT+="\n$(terraform --version)"
- VERSION_OUTPUT+="\n$(platform-helper --version)"
Expand Down
4 changes: 2 additions & 2 deletions environment-pipelines/buildspec-trigger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ phases:
- 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}"
- aws codepipeline start-pipeline-execution --name "${TRIGGERED_PIPELINE_NAME}" --profile "${PROFILE_NAME}" --variables name=SLACK_THREAD_ID,value=${SLACK_REF}

post_build:
commands:
- export ACCOUNT_NAME="${TRIGGERED_PIPELINE_AWS_PROFILE}"
- |
if [ "${CODEBUILD_BUILD_SUCCEEDING}" == "1" ]
then
Expand Down
107 changes: 87 additions & 20 deletions environment-pipelines/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ data "aws_iam_policy_document" "assume_codebuild_role" {

actions = ["sts:AssumeRole"]
}

dynamic "statement" {
for_each = toset(local.triggers_another_pipeline ? [""] : [])
content {
effect = "Allow"

principals {
type = "AWS"
identifiers = [local.triggered_pipeline_codebuild_role]
}

actions = ["sts:AssumeRole"]
}
}
}

data "aws_iam_policy_document" "write_environment_pipeline_codebuild_logs" {
Expand Down Expand Up @@ -716,6 +730,18 @@ data "aws_iam_policy_document" "copilot_assume_role" {
]
}
}

dynamic "statement" {
for_each = toset(local.triggers_another_pipeline ? local.triggered_pipeline_environments : [])
content {
actions = [
"sts:AssumeRole"
]
resources = [
"arn:aws:iam::${local.triggered_account_id}:role/${var.application}-${statement.value.name}-EnvManagerRole"
]
}
}
}

data "aws_iam_policy_document" "cloudformation" {
Expand All @@ -731,6 +757,8 @@ data "aws_iam_policy_document" "cloudformation" {
"cloudformation:DescribeChangeSet",
"cloudformation:CreateChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:DescribeStackEvents",
"cloudformation:DeleteStack"
]
resources = [
"arn:aws:cloudformation:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:stack/${var.application}-*",
Expand All @@ -748,26 +776,31 @@ resource "aws_iam_policy" "cloudformation" {
}

data "aws_iam_policy_document" "iam" {
statement {
actions = [
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:CreatePolicy",
"iam:DeletePolicy",
"iam:CreateRole",
"iam:DeleteRole",
"iam:TagRole",
"iam:PutRolePolicy",
"iam:GetRole",
"iam:ListRolePolicies",
"iam:GetRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:DeleteRolePolicy",
]
resources = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*-${var.application}-*-conduitEcsTask",
]
dynamic "statement" {
for_each = local.environment_config
content {
actions = [
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:CreatePolicy",
"iam:DeletePolicy",
"iam:CreateRole",
"iam:DeleteRole",
"iam:TagRole",
"iam:PutRolePolicy",
"iam:GetRole",
"iam:ListRolePolicies",
"iam:GetRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:DeleteRolePolicy",
]
resources = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*-${var.application}-*-conduitEcsTask",
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.application}-${statement.value.name}-CFNExecutionRole",
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.application}-${statement.value.name}-EnvManagerRole"
]
}
}
}

Expand Down Expand Up @@ -962,6 +995,40 @@ data "aws_iam_policy_document" "trigger_pipeline" {
}
}

resource "aws_iam_role_policy" "assume_role_for_copilot_env_commands" {
for_each = toset(local.triggered_by_another_pipeline ? [""] : [])
name = "${var.application}-${var.pipeline_name}-assume-role-for-copilot-env-commands"
role = aws_iam_role.environment_pipeline_codebuild.name
policy = data.aws_iam_policy_document.assume_role_for_copilot_env_commands_policy_document[""].json
}

data "aws_iam_policy_document" "assume_role_for_copilot_env_commands_policy_document" {
for_each = toset(local.triggered_by_another_pipeline ? [""] : [])
statement {
actions = [
"sts:AssumeRole"
]
resources = local.triggering_pipeline_role_arns
}

statement {
actions = [
"kms:*",
]
resources = [
"arn:aws:kms:${data.aws_region.current.name}:${local.triggering_account_id}:key/*"
]
}

statement {
actions = [
"s3:*",
]
resources = [
"arn:aws:s3:::stackset-${var.application}-*-pipelinebuiltartifactbuc-*"
]
}
}

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

Expand Down
24 changes: 15 additions & 9 deletions environment-pipelines/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,23 @@ locals {
account_map = { for account in local.extracted_account_names_and_ids : account["name"] => account["id"] }

# Convert the env config into a list and add env name and vpc / requires_approval from the environments config.
environment_config = [for name, env in var.environments : merge(lookup(local.base_env_config, name, {}), env, { "name" = name })]
triggers_another_pipeline = var.pipeline_to_trigger != null
environment_config = [for name, env in var.environments : merge(lookup(local.base_env_config, name, {}), env, { "name" = name })]

triggered_pipeline_account_name = local.triggers_another_pipeline ? var.all_pipelines[var.pipeline_to_trigger].account : null
triggered_account_id = local.triggers_another_pipeline ? local.account_map[local.triggered_pipeline_account_name] : null
triggers_another_pipeline = var.pipeline_to_trigger != null
triggered_pipeline_account_name = local.triggers_another_pipeline ? var.all_pipelines[var.pipeline_to_trigger].account : null
triggered_account_id = local.triggers_another_pipeline ? local.account_map[local.triggered_pipeline_account_name] : null
triggered_pipeline_codebuild_role = local.triggers_another_pipeline ? "arn:aws:iam::${local.triggered_account_id}:role/${var.application}-${var.pipeline_to_trigger}-environment-pipeline-codebuild" : null
triggered_pipeline_environments = local.triggers_another_pipeline ? [for name, config in var.all_pipelines[var.pipeline_to_trigger].environments : { "name" = name }] : null

list_of_triggering_pipelines = [for pipeline, config in var.all_pipelines : merge(config, { name = pipeline }) if lookup(config, "pipeline_to_trigger", null) == var.pipeline_name]
set_of_triggering_pipeline_names = toset([for pipeline in local.list_of_triggering_pipelines : pipeline.name])
triggering_pipeline_role_arns = [for name in local.set_of_triggering_pipeline_names : "arn:aws:iam::${local.account_map[var.all_pipelines[name].account]}:role/${var.application}-${name}-environment-pipeline-codebuild"]

triggering_pipeline_role_arns = [for name in local.set_of_triggering_pipeline_names : "arn:aws:iam::${local.account_map[var.all_pipelines[name].account]}:role/${var.application}-${name}-environment-pipeline-codebuild"]
triggered_by_another_pipeline = length([for config in var.all_pipelines : true if lookup(config, "pipeline_to_trigger", null) == var.pipeline_name]) > 0
triggering_pipeline_account_name = local.triggered_by_another_pipeline ? one(local.list_of_triggering_pipelines).account : null
triggering_account_id = local.triggered_by_another_pipeline ? local.account_map[local.triggering_pipeline_account_name] : null
triggering_pipeline_name = local.triggered_by_another_pipeline ? one(local.list_of_triggering_pipelines).name : null
triggering_pipeline_codebuild_role = local.triggered_by_another_pipeline ? "arn:aws:iam::${local.triggering_account_id}:role/${var.application}-${local.triggering_pipeline_name}-environment-pipeline-codebuild" : null


initial_stages = [for env in local.environment_config : [
Expand All @@ -49,7 +56,6 @@ locals {
{ name : "APPLICATION", value : var.application },
{ name : "ENVIRONMENT", value : env.name },
{ name : "PIPELINE_NAME", value : var.pipeline_name },
{ name : "COPILOT_PROFILE", value : env.accounts.deploy.name },
{ name : "SLACK_CHANNEL_ID", value : var.slack_channel, type : "PARAMETER_STORE" },
{ name : "SLACK_REF", value : "#{slack.SLACK_REF}" },
{ name : "NEEDS_APPROVAL", value : lookup(env, "requires_approval", false) ? "yes" : "no" },
Expand Down Expand Up @@ -84,10 +90,12 @@ locals {
PrimarySource : "${env.name}_terraform_plan"
EnvironmentVariables : jsonencode([
{ name : "ENVIRONMENT", value : env.name },
{ name : "AWS_PROFILE_FOR_COPILOT", value : env.accounts.deploy.name },
{ name : "SLACK_CHANNEL_ID", value : var.slack_channel, type : "PARAMETER_STORE" },
{ name : "SLACK_REF", value : "#{slack.SLACK_REF}" },
{ name : "VPC", value : local.base_env_config[env.name].vpc },
{ name : "SLACK_THREAD_ID", value : "#{variables.SLACK_THREAD_ID}" },
local.triggered_by_another_pipeline ? { name : "TRIGGERING_ACCOUNT_CODEBUILD_ROLE", value : local.triggering_pipeline_codebuild_role } : null,
local.triggered_by_another_pipeline ? { name : "TRIGGERING_ACCOUNT_AWS_PROFILE", value : local.triggering_pipeline_account_name } : null,
])
},
namespace : null
Expand All @@ -98,7 +106,6 @@ locals {
triggered_pipeline_account_role = local.triggers_another_pipeline ? "arn:aws:iam::${local.triggered_account_id}:role/${var.application}-${var.pipeline_to_trigger}-trigger-pipeline-from-${var.pipeline_name}" : null
target_pipeline = local.triggers_another_pipeline ? "${var.application}-${var.pipeline_to_trigger}-environment-pipeline" : null


all_stages = flatten(
concat(local.initial_stages, local.triggers_another_pipeline ? [
{
Expand All @@ -116,7 +123,6 @@ locals {
{ name : "SLACK_THREAD_ID", value : "#{variables.SLACK_THREAD_ID}" },
{ name : "SLACK_CHANNEL_ID", value : var.slack_channel, type : "PARAMETER_STORE" },
{ name : "SLACK_REF", value : "#{slack.SLACK_REF}" },
{ name : "ACCOUNT_NAME", value : local.triggered_pipeline_account_name },
])
},
namespace : null
Expand Down
Loading

0 comments on commit ea3b56d

Please sign in to comment.