|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +AWS_DEFAULT_REGION="eu-west-2" |
| 4 | +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" |
| 5 | +DOJO_IMAGE_VERSION="nhsdev/deductions-infra-dojo:a3ce7038c095298273348419769e327039009bf5" |
| 6 | + |
| 7 | +readonly EXIT_CODE_GENERAL_FAILURE=5 |
| 8 | +readonly EXIT_CODE_DONT_USE_NHSDADMINROLE=6 |
| 9 | +readonly EXIT_CODE_USERS_SHOULD_ASSUME_ROLE_FIRST=7 |
| 10 | +readonly EXIT_CODE_UNHANDLED_IDENTITY=18 |
| 11 | + |
| 12 | +function get_latest_commit_hash { |
| 13 | + export COMMIT_HASH=$(git rev-parse HEAD | cut -c 1-7) |
| 14 | +} |
| 15 | + |
| 16 | +function create_build_trace_id { |
| 17 | + if [ -z $GO_PIPELINE_NAME ]; then |
| 18 | + export BUILD_TRACE_ID=local |
| 19 | + else |
| 20 | + git_hash=$(echo $GO_REVISION_GIT | cut -c 1-8) |
| 21 | + candidate_build_trace_id="$GO_PIPELINE_NAME@$GO_PIPELINE_COUNTER@$GO_STAGE_NAME@$GO_STAGE_COUNTER@$GO_JOB_NAME@$git_hash" |
| 22 | + |
| 23 | + export BUILD_TRACE_ID=$(echo "$candidate_build_trace_id" | awk '{ print substr( $0, length($0) - 62, length($0) ) }' | sed "s/^[-]*//") |
| 24 | + fi |
| 25 | +} |
| 26 | + |
| 27 | +function get_aws_ssm_secret { |
| 28 | + secret_id=$1 |
| 29 | + json=$(dojo -image "$DOJO_IMAGE_VERSION" "aws ssm get-parameter --with-decryption --region eu-west-2 --name $secret_id") |
| 30 | + if [ $? != 0 ]; then |
| 31 | + >&2 echo "Failed to obtain AWS secret from SSM: $secret_id" |
| 32 | + exit $EXIT_CODE_GENERAL_FAILURE |
| 33 | + fi |
| 34 | + echo $json | jq -r ".Parameter.Value" |
| 35 | +} |
| 36 | + |
| 37 | +function _get_aws_ssm_secret { |
| 38 | + secret_id=$1 |
| 39 | + json=$(aws ssm get-parameter --with-decryption --region "eu-west-2" --name $secret_id) |
| 40 | + if [ $? != 0 ]; then |
| 41 | + >&2 echo "Failed to obtain AWS secret from SSM: $secret_id" |
| 42 | + exit $EXIT_CODE_GENERAL_FAILURE |
| 43 | + fi |
| 44 | + echo $json | jq -r ".Parameter.Value" |
| 45 | +} |
| 46 | + |
| 47 | +function docker_login { |
| 48 | + >&2 echo Logging in to Amazon ECR... |
| 49 | + eval $(dojo -image "$DOJO_IMAGE_VERSION" "aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION") |
| 50 | +} |
| 51 | + |
| 52 | +function _get_aws_account_id { |
| 53 | + aws sts get-caller-identity | jq -r .Account |
| 54 | +} |
| 55 | + |
| 56 | +function get_aws_account_id { |
| 57 | + AWS_ACCOUNT_ID=$(dojo -image "$DOJO_IMAGE_VERSION" "aws sts get-caller-identity | jq -r .Account") |
| 58 | +} |
| 59 | + |
| 60 | +function configure_docker_repository_uri { |
| 61 | + docker_login |
| 62 | + get_aws_account_id |
| 63 | + export REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/$IMAGE_REPO_NAME |
| 64 | +} |
| 65 | + |
| 66 | +function configure_docker_registry_uri { |
| 67 | + docker_login |
| 68 | + get_aws_account_id |
| 69 | + export REGISTRY_URI=$AWS_ACCOUNT_ID.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com |
| 70 | +} |
| 71 | + |
| 72 | +function assume_environment_role { |
| 73 | + CURRENT_DIR=$(pwd) |
| 74 | + cd "$SCRIPT_DIR" |
| 75 | + eval $(dojo -image "$DOJO_IMAGE_VERSION" "./aws-helpers assume_environment_role $1") |
| 76 | + cd "$CURRENT_DIR" |
| 77 | +} |
| 78 | + |
| 79 | +function _clear_aws_env_credentials { |
| 80 | + unset AWS_ACCESS_KEY_ID |
| 81 | + unset AWS_SECRET_ACCESS_KEY |
| 82 | + unset AWS_SESSION_TOKEN |
| 83 | + unset AWS_SECURITY_TOKEN |
| 84 | +} |
| 85 | + |
| 86 | +function _assume_environment_role { |
| 87 | + # Agent always needs to assume Deployer role in one of the destination accounts |
| 88 | + # Deployer needs to assume role back in CI for |
| 89 | + # People need to assume RepoAdmin role in early, non-strict (dev/test) accounts |
| 90 | + # People need to assume BootstrapAdmin or RepoDeveloper role in prod-like, strict (pre-prod/prod) accounts |
| 91 | + # The starting role for the agent is gocd_agent-prod in the CI account |
| 92 | + # The starting role for people is their personal user in the nhsd-identities account |
| 93 | + environment=$1 |
| 94 | + is_bootstrap_admin=$2 |
| 95 | + current_identity_arn=$(aws sts get-caller-identity | jq -r .Arn) |
| 96 | + create_build_trace_id |
| 97 | + |
| 98 | + if [[ $current_identity_arn =~ "gocd_agent-prod" || $current_identity_arn =~ "Deployer" ]]; then |
| 99 | + assume_role_for_ci_agent "${environment}" |
| 100 | + elif [[ $current_identity_arn =~ "user" || $current_identity_arn =~ "RepoAdmin" || $current_identity_arn =~ "BootstrapAdmin" || $current_identity_arn =~ "RepoDeveloper" ]]; then |
| 101 | + assume_role_for_user "${environment}" "${current_identity_arn}" "${is_bootstrap_admin}" |
| 102 | + elif [[ $current_identity_arn =~ "NHSDAdminRole" ]]; then |
| 103 | + >&2 echo "You are using NHSDAdminRole which is now deprecated for general use. Clear assumed role with:" |
| 104 | + >&2 echo " unset AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_ACCESS_KEY_ID AWS_SECURITY_TOKEN" |
| 105 | + >&2 echo "You should assume role direct from your user identity." |
| 106 | + exit $EXIT_CODE_DONT_USE_NHSDADMINROLE |
| 107 | + else |
| 108 | + >&2 echo "_assume_environment_role does not recognise current unhandled identity $current_identity_arn." |
| 109 | + exit $EXIT_CODE_UNHANDLED_IDENTITY |
| 110 | + fi |
| 111 | + |
| 112 | + if [[ ! -z $json ]]; then |
| 113 | + export AWS_ACCESS_KEY_ID="$(echo "$json" | jq -r .Credentials.AccessKeyId)" |
| 114 | + export AWS_SECRET_ACCESS_KEY="$(echo "$json" | jq -r .Credentials.SecretAccessKey)" |
| 115 | + export AWS_SESSION_TOKEN="$(echo "$json" | jq -r .Credentials.SessionToken)" |
| 116 | + export AWS_SECURITY_TOKEN=$AWS_SESSION_TOKEN |
| 117 | + fi |
| 118 | +} |
| 119 | + |
| 120 | +function assume_role_for_ci_agent { |
| 121 | + environment=$1 |
| 122 | + _clear_aws_env_credentials |
| 123 | + |
| 124 | + ROLE_NAME='Deployer' |
| 125 | + ASSUME_ROLE_DURATION_CLAUSE='' |
| 126 | + if [ -n "$ASSUME_ROLE_DURATION" ]; then |
| 127 | + ASSUME_ROLE_DURATION_CLAUSE="--duration-seconds $ASSUME_ROLE_DURATION" |
| 128 | + fi |
| 129 | + |
| 130 | + >&2 echo "Assuming $ROLE_NAME role for CI agent in ${environment} account" |
| 131 | + desired_account_id=$(_get_aws_ssm_secret "/repo/${environment}/user-input/external/aws-account-id") |
| 132 | + desired_role_arn="arn:aws:iam::$desired_account_id:role/$ROLE_NAME" |
| 133 | + >&2 echo "Build trace ID will be set as STS session name: $BUILD_TRACE_ID" |
| 134 | + |
| 135 | + json="$(aws sts assume-role --role-arn "$desired_role_arn" --role-session-name "$BUILD_TRACE_ID" $ASSUME_ROLE_DURATION_CLAUSE)" |
| 136 | +} |
| 137 | + |
| 138 | +function prompt_user_to_assume_role { |
| 139 | + environment=$1 |
| 140 | + role_name=$2 |
| 141 | + echo Please assume $role_name in $environment directly from your shell |
| 142 | +} |
| 143 | + |
| 144 | +function assume_role_for_user { |
| 145 | + environment=$1 |
| 146 | + current_identity_arn=$2 |
| 147 | + is_bootstrap_admin=$3 |
| 148 | + if [[ ! $current_identity_arn =~ "user" ]]; then |
| 149 | + return |
| 150 | + fi |
| 151 | + |
| 152 | + if [[ $environment =~ "prod" ]]; then |
| 153 | + if [[ $is_bootstrap_admin = "true" ]]; then |
| 154 | + ROLE_NAME="BootstrapAdmin" |
| 155 | + else |
| 156 | + ROLE_NAME="RepoDeveloper" |
| 157 | + fi |
| 158 | + else |
| 159 | + ROLE_NAME="RepoAdmin" |
| 160 | + fi |
| 161 | + |
| 162 | + prompt_user_to_assume_role $environment $ROLE_NAME |
| 163 | + exit $EXIT_CODE_USERS_SHOULD_ASSUME_ROLE_FIRST |
| 164 | +} |
| 165 | + |
| 166 | +function promote_docker_image { |
| 167 | + # e.g. deductions/ehr-repo:1ab321 |
| 168 | + SRC_IMAGE_NAME_AND_TAG=$1 |
| 169 | + ENVIRONMENT=$2 |
| 170 | + if [ -z "$SRC_IMAGE_NAME_AND_TAG" ]; then |
| 171 | + >&2 echo "Image name and tag must be specified. e.g. deductions/ehr-repo:1ab321" |
| 172 | + exit $EXIT_CODE_GENERAL_FAILURE; |
| 173 | + fi |
| 174 | + if [ -z "$ENVIRONMENT" ]; then |
| 175 | + >&2 echo "Environment must be specified. e.g. dev" |
| 176 | + exit $EXIT_CODE_GENERAL_FAILURE; |
| 177 | + fi |
| 178 | + declare -A from_environment_promotion_map=(["dev"]="ci" ["test"]="dev" ["pre-prod"]="test" ["prod"]="pre-prod" ["perf"]="test") |
| 179 | + declare -A to_environment_promotion_map=(["dev"]="dev" ["test"]="test" ["pre-prod"]="pre-prod" ["prod"]="prod" ["perf"]="perf") |
| 180 | + |
| 181 | + environment_from=${from_environment_promotion_map[$ENVIRONMENT]} |
| 182 | + environment_to=${to_environment_promotion_map[$ENVIRONMENT]} |
| 183 | + |
| 184 | + echo >&2 "Promoting docker image from $environment_from to $environment_to..." |
| 185 | + |
| 186 | + assume_environment_role "$environment_from" |
| 187 | + configure_docker_registry_uri |
| 188 | + IMAGE_FULL_URL="$REGISTRY_URI/$SRC_IMAGE_NAME_AND_TAG" |
| 189 | + echo "Pulling the image from $IMAGE_FULL_URL" |
| 190 | + docker pull "$IMAGE_FULL_URL" |
| 191 | + docker tag "$IMAGE_FULL_URL" "$SRC_IMAGE_NAME_AND_TAG" |
| 192 | + |
| 193 | + assume_environment_role "$environment_to" |
| 194 | + configure_docker_registry_uri |
| 195 | + IMAGE_FULL_URL="$REGISTRY_URI/$SRC_IMAGE_NAME_AND_TAG" |
| 196 | + docker tag "$SRC_IMAGE_NAME_AND_TAG" "$IMAGE_FULL_URL" |
| 197 | + echo "Pushing the image to $IMAGE_FULL_URL" |
| 198 | + docker push "$IMAGE_FULL_URL" |
| 199 | +} |
| 200 | + |
| 201 | +########### |
| 202 | +## TASKS ## |
| 203 | +########### |
| 204 | + |
| 205 | +command="$1" |
| 206 | +case "${command}" in |
| 207 | + assume_environment_role) |
| 208 | + _assume_environment_role $2 |
| 209 | + echo "export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" |
| 210 | + echo "export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" |
| 211 | + echo "export AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" |
| 212 | + echo "export AWS_SECURITY_TOKEN=$AWS_SESSION_TOKEN" |
| 213 | + ;; |
| 214 | + test_ass_duration) |
| 215 | + assume_role_for_ci_agent dev |
| 216 | + ;; |
| 217 | +esac |
0 commit comments