From 60d1885add06f3b3f60712c254c05721905c72ce Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:15:33 -0600 Subject: [PATCH 01/20] refactor branch validation and variable defaults --- cicd/2-cicd/deploy-cicd.sh | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/cicd/2-cicd/deploy-cicd.sh b/cicd/2-cicd/deploy-cicd.sh index 357dca8..bf46edc 100755 --- a/cicd/2-cicd/deploy-cicd.sh +++ b/cicd/2-cicd/deploy-cicd.sh @@ -8,38 +8,39 @@ echo Deploying AiProxy CICD Pipeline # - ENVIRONMENT_TYPE: Can be 'production' (default) or 'development', passed as a Parameter for "cicd/2-cicd/cicd.template.yml" # - GITHUB_BADGE_ENABLED: defaults to true, passed as a Parameter for "cicd/2-cicd/cicd.template.yml" +# The branch name may become part of a domain name, so we need to validate it. +validate_branch_name() { + local branch_name=$1 + if [[ ! $branch_name =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])$ ]]; then + echo "Invalid branch name '${branch_name}', branches must be alphanumeric and may contain hyphens." + exit 1 + fi +} + # 'Developer' role requires a specific service role for all CloudFormation operations. if [[ $(aws sts get-caller-identity --query Arn --output text) =~ "assumed-role/Developer/" ]]; then # Append the role-arn option to the positional parameters $@ passed to cloudformation deploy. set -- "$@" --role-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/admin/CloudFormationService" fi -# Default to main branch, but support pipelines using other branches -TARGET_BRANCH=${TARGET_BRANCH-'main'} +ENVIRONMENT_TYPE=${ENVIRONMENT_TYPE:-'production'} +GITHUB_BADGE_ENABLED=${GITHUB_BADGE_ENABLED:-'true'} +TARGET_BRANCH=${TARGET_BRANCH:-'main'} if [ "$TARGET_BRANCH" == "main" ] then STACK_NAME="aiproxy-cicd" else - # only allow alphanumeric branch names that may contain an internal hyphen. - # to avoid complicated logic elsewhere, we're constraining it here. - if [[ "$TARGET_BRANCH" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])$ ]]; then - STACK_NAME="aiproxy-${TARGET_BRANCH}-cicd" - else - echo "Invalid branch name '${TARGET_BRANCH}', branches must be alphanumeric and may contain hyphens." - exit - fi + validate_branch_name "$TARGET_BRANCH" + STACK_NAME="aiproxy-${TARGET_BRANCH}-cicd" fi -ENVIRONMENT_TYPE=${ENVIRONMENT_TYPE-'production'} -GITHUB_BADGE_ENABLED=${GITHUB_BADGE_ENABLED-'true'} - TEMPLATE_FILE=cicd/2-cicd/cicd.template.yml echo Validating cloudformation template... aws cloudformation validate-template \ --template-body file://${TEMPLATE_FILE} \ - | cat + > /dev/null ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) From db4d85dcac719b549ecec382323dc8ba6fbd101d Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:15:58 -0600 Subject: [PATCH 02/20] create reviewable change set instead of blind deploy --- cicd/2-cicd/deploy-cicd.sh | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/cicd/2-cicd/deploy-cicd.sh b/cicd/2-cicd/deploy-cicd.sh index bf46edc..9669a4d 100755 --- a/cicd/2-cicd/deploy-cicd.sh +++ b/cicd/2-cicd/deploy-cicd.sh @@ -43,20 +43,36 @@ aws cloudformation validate-template \ > /dev/null ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) +REGION=$(aws configure get region) -read -r -p "Would you like to deploy this template to AWS account $ACCOUNT? [y/N] " response +read -r -p "Would you like to create a change set for this template in AWS account $ACCOUNT? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] then - echo Updating cloudformation stack... - aws cloudformation deploy \ + echo Creating change set... + CHANGE_SET_NAME="${STACK_NAME}-changeset-$(date +%s)" + aws cloudformation create-change-set \ --stack-name $STACK_NAME \ - --template-file $TEMPLATE_FILE \ - --parameter-overrides GitHubBranch=$TARGET_BRANCH GitHubBadgeEnabled=$GITHUB_BADGE_ENABLED EnvironmentType=$ENVIRONMENT_TYPE \ + --change-set-name $CHANGE_SET_NAME \ + --template-body file://${TEMPLATE_FILE} \ + --parameters ParameterKey=GitHubBranch,ParameterValue=$TARGET_BRANCH ParameterKey=GitHubBadgeEnabled,ParameterValue=$GITHUB_BADGE_ENABLED ParameterKey=EnvironmentType,ParameterValue=$ENVIRONMENT_TYPE \ --capabilities CAPABILITY_IAM \ - --tags EnvType=${ENVIRONMENT_TYPE} \ + --tags Key=EnvType,Value=${ENVIRONMENT_TYPE} \ "$@" - echo Complete! + echo "Change set created. You can review it at:" + echo "https://console.aws.amazon.com/cloudformation/home?region=$REGION#/stacks/changesets/changes?stackId=arn:aws:cloudformation:$REGION:$ACCOUNT:stack/$STACK_NAME/*&changeSetId=arn:aws:cloudformation:$REGION:$ACCOUNT:changeSet/$CHANGE_SET_NAME" + + read -r -p "Would you like to execute the change set? [y/N] " response + if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] + then + echo Executing change set... + aws cloudformation execute-change-set \ + --stack-name $STACK_NAME \ + --change-set-name $CHANGE_SET_NAME + echo Complete! + else + echo Exiting... + fi else echo Exiting... fi From 8dcffc0a6abcf35da1f90b50f20b151e3c113c74 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:18:09 -0600 Subject: [PATCH 03/20] create reviewable change set instead of blind deploy --- cicd/1-setup/deploy-cicd-dependencies.sh | 31 ++++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cicd/1-setup/deploy-cicd-dependencies.sh b/cicd/1-setup/deploy-cicd-dependencies.sh index 735e57f..babf20b 100755 --- a/cicd/1-setup/deploy-cicd-dependencies.sh +++ b/cicd/1-setup/deploy-cicd-dependencies.sh @@ -11,23 +11,38 @@ TEMPLATE_FILE=cicd/1-setup/cicd-dependencies.template.yml echo Validating cloudformation template... aws cloudformation validate-template \ --template-body file://${TEMPLATE_FILE} \ - | cat + > /dev/null ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) +REGION=$(aws configure get region) -read -r -p "Would you like to deploy this template to AWS account $ACCOUNT? [y/N] " response +read -r -p "Would you like to create a change set for this template in AWS account $ACCOUNT? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] then - echo Updating cloudformation stack... - echo "Follow along at https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks?filteringText=aiproxy-cicd-deps&filteringStatus=active&viewNested=true&hideStacks=false" - aws cloudformation deploy \ + echo Creating change set... + CHANGE_SET_NAME="aiproxy-cicd-deps-changeset-$(date +%s)" + aws cloudformation create-change-set \ --stack-name aiproxy-cicd-deps \ - --template-file ${TEMPLATE_FILE} \ + --change-set-name $CHANGE_SET_NAME \ + --template-body file://${TEMPLATE_FILE} \ --capabilities CAPABILITY_IAM \ --tags EnvType=infrastructure \ "$@" - echo Complete! + echo "Change set created. You can review it at:" + echo "https://console.aws.amazon.com/cloudformation/home?region=$REGION#/stacks/changesets/changes?stackId=arn:aws:cloudformation:$REGION:$ACCOUNT:stack/aiproxy-cicd-deps/*&changeSetId=arn:aws:cloudformation:$REGION:$ACCOUNT:changeSet/$CHANGE_SET_NAME" + + read -r -p "Would you like to execute the change set? [y/N] " response + if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] + then + echo Executing change set... + aws cloudformation execute-change-set \ + --stack-name aiproxy-cicd-deps \ + --change-set-name $CHANGE_SET_NAME + echo Complete! + else + echo Exiting... + fi else echo Exiting... -fi +fi \ No newline at end of file From 99f251ff877460465d0ca40eb6bbcc2005170d7f Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:19:17 -0600 Subject: [PATCH 04/20] remove permissions boundary --- cicd/1-setup/cicd-dependencies.template.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/cicd/1-setup/cicd-dependencies.template.yml b/cicd/1-setup/cicd-dependencies.template.yml index 5744157..4807c7a 100644 --- a/cicd/1-setup/cicd-dependencies.template.yml +++ b/cicd/1-setup/cicd-dependencies.template.yml @@ -27,7 +27,6 @@ Resources: - codepipeline.amazonaws.com Version: '2012-10-17' Path: /service-role/ - PermissionsBoundary: !ImportValue IAM-DevPermissions Policies: - PolicyName: AiProxyPassRole PolicyDocument: @@ -103,7 +102,6 @@ Resources: - codebuild.amazonaws.com Version: '2012-10-17' Path: /service-role/ - PermissionsBoundary: !ImportValue IAM-DevPermissions Policies: - PolicyName: PublicCodeBuildSecretsAccess PolicyDocument: From ac1ffa14ed2d9448a7771197571799f900ca970f Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:19:44 -0600 Subject: [PATCH 05/20] tweak comments --- cicd/1-setup/cicd-dependencies.template.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cicd/1-setup/cicd-dependencies.template.yml b/cicd/1-setup/cicd-dependencies.template.yml index 4807c7a..78f08ed 100644 --- a/cicd/1-setup/cicd-dependencies.template.yml +++ b/cicd/1-setup/cicd-dependencies.template.yml @@ -42,7 +42,7 @@ Resources: - "cloudformation:DescribeStacks" - "cloudformation:CreateStack" - "cloudformation:UpdateStack" - Resource: "*" + Resource: "*" # TODO scope to specific stacks - PolicyName: CodeBuildResourcesAccess PolicyDocument: Statement: @@ -67,11 +67,10 @@ Resources: - s3:GetObjectVersion Resource: - !Sub ${ArtifactStore.Arn}/* - # TODO: Scope to specific ECR Repos? - Effect: Allow Action: - ecr:GetAuthorizationToken - Resource: '*' + Resource: '*' # TODO scope to specific ECR repos - Effect: Allow Action: codestar-connections:UseConnection Resource: From 22f56bedff91c51d65454c9f3fd9cb41debe98cb Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:21:37 -0600 Subject: [PATCH 06/20] eliminate ImportValue from app template --- cicd/2-cicd/cicd.template.yml | 66 +++++++++++++++++++++++----- cicd/3-app/aiproxy/template.yml | 78 ++++++++++++++++++++++++--------- 2 files changed, 114 insertions(+), 30 deletions(-) diff --git a/cicd/2-cicd/cicd.template.yml b/cicd/2-cicd/cicd.template.yml index 3262960..b97f5f9 100644 --- a/cicd/2-cicd/cicd.template.yml +++ b/cicd/2-cicd/cicd.template.yml @@ -278,11 +278,25 @@ Resources: ActionMode: CREATE_UPDATE TemplatePath: appBuildResults::packaged-app-template.yml TemplateConfiguration: appBuildResults::cicd/3-app/aiproxy/dev.config.json + # ParameterOverrides must be a JSON string, not an object ParameterOverrides: !Join - - '' - - - '{ "SubdomainName": "' - - !Sub "aiproxy-dev-${GitHubBranch}" - - '" }' + - '' + - - '{' + - !Sub '"SubdomainName": "aiproxy-dev-${GitHubBranch}",' + - !Sub '"ECRRepositoryArn": "${EcrRepository.Arn}",' + - !Sub + - '"VPC": "${VPCValue}",' + - { VPCValue: !ImportValue VPC } + - !Sub + - '"SecurityGroup": "${SecurityGroupValue}",' + - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } + - !Sub + - '"PublicSubnets": ${PublicSubnetsValue},' + - { PublicSubnetsValue: [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE] } + - !Sub + - '"PrivateSubnets": ${PrivateSubnetsValue},' + - { PrivateSubnetsValue: [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE] } + - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/admin/CloudFormationService - !Ref AWS::NoValue @@ -304,11 +318,27 @@ Resources: ActionMode: CREATE_UPDATE TemplatePath: appBuildResults::packaged-app-template.yml TemplateConfiguration: appBuildResults::cicd/3-app/aiproxy/test.config.json + # ParameterOverrides must be a JSON string, not an object ParameterOverrides: !Join - '' - - - '{ "SubdomainName": "' - - !If [ TargetsMainBranch, 'aiproxy-test', !Sub 'aiproxy-test-${GitHubBranch}' ] - - '" }' + - - '{' + - !Sub + - '"SubdomainName": "${SubdomainName}",' + - { SubdomainName: !If [ TargetsMainBranch, 'aiproxy-test', !Sub 'aiproxy-test-${GitHubBranch}' ] } + - !Sub '"ECRRepositoryArn": "${EcrRepository.Arn}",' + - !Sub + - '"VPC": "${VPCValue}",' + - { VPCValue: !ImportValue VPC } + - !Sub + - '"SecurityGroup": "${SecurityGroupValue}",' + - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } + - !Sub + - '"PublicSubnets": ${PublicSubnetsValue},' + - { PublicSubnetsValue: [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE] } + - !Sub + - '"PrivateSubnets": ${PrivateSubnetsValue},' + - { PrivateSubnetsValue: [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE] } + - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/admin/CloudFormationService - !Ref AWS::NoValue @@ -353,11 +383,27 @@ Resources: ActionMode: CREATE_UPDATE TemplatePath: appBuildResults::packaged-app-template.yml TemplateConfiguration: appBuildResults::cicd/3-app/aiproxy/production.config.json + # ParameterOverrides must be a JSON string, not an object ParameterOverrides: !Join - '' - - - '{ "SubdomainName": "' - - !If [ TargetsMainBranch, 'aiproxy', !Sub 'aiproxy-${GitHubBranch}' ] - - '" }' + - - '{' + - !Sub + - '"SubdomainName": "${SubdomainName}",' + - { SubdomainName: !If [ TargetsMainBranch, 'aiproxy', !Sub 'aiproxy-${GitHubBranch}' ] } + - !Sub '"ECRRepositoryArn": "${EcrRepository.Arn}",' + - !Sub + - '"VPC": "${VPCValue}",' + - { VPCValue: !ImportValue VPC } + - !Sub + - '"SecurityGroup": "${SecurityGroupValue}",' + - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } + - !Sub + - '"PublicSubnets": ${PublicSubnetsValue},' + - { PublicSubnetsValue: [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE] } + - !Sub + - '"PrivateSubnets": ${PrivateSubnetsValue},' + - { PrivateSubnetsValue: [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE] } + - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/admin/CloudFormationService - !Ref AWS::NoValue diff --git a/cicd/3-app/aiproxy/template.yml b/cicd/3-app/aiproxy/template.yml index 5b87233..c0ccc61 100644 --- a/cicd/3-app/aiproxy/template.yml +++ b/cicd/3-app/aiproxy/template.yml @@ -2,8 +2,6 @@ AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: Provision an instance of the AI Proxy service. -# Dependencies: This template has dependencies, look for !ImportValue in the Resources section. - Parameters: BaseDomainName: Type: String @@ -14,9 +12,20 @@ Parameters: SubdomainName: Type: String Description: Subdomain name for aiproxy service (e.g. 'aiproxy' in 'aiproxy.code.org'). + ECRRepositoryArn: + Type: String + Description: ARN of the ECR repository for this service. AppImageUri: Type: String Description: URI of the Docker image in ECR. + VPC: + Type: AWS::EC2::VPC::Id + SecurityGroup: + Type: AWS::EC2::SecurityGroup::Id + PublicSubnets: + Type: List + PrivateSubnets: + Type: List # Conditions: # IsDevCondition: !Equals [!Ref BaseDomainName, "dev-code.org"] @@ -63,16 +72,11 @@ Resources: LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: 180 - SecurityGroups: - - !ImportValue VPC-ELBSecurityGroup - Subnets: - # Place load balancer in public subnets, so it's accessible from the internet. - # We may want to move this to the private subnets, so only internal resources - # can access it, but this is very convenient for local development. - - !ImportValue VPC-PublicSubnetB - - !ImportValue VPC-PublicSubnetC - - !ImportValue VPC-PublicSubnetD - - !ImportValue VPC-PublicSubnetE + SecurityGroups: !Ref LoadBalancerSecurityGroups + # Place load balancer in public subnets, so it's accessible from the internet. + # We may want to move this to the private subnets, so only internal resources + # can access it, but this is very convenient for local development. + Subnets: !Ref PublicSubnets HttpListener: Type: AWS::ElasticLoadBalancingV2::Listener @@ -114,7 +118,7 @@ Resources: TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: - VpcId: !ImportValue VPC + VpcId: !Ref VPC Port: 80 TargetType: ip Protocol: HTTP @@ -147,12 +151,8 @@ Resources: AssignPublicIp: DISABLED SecurityGroups: - !Ref ECSSecurityGroup - Subnets: # Place ECS Service in private subnets, but traffic should use the LoadBalancer. - - !ImportValue VPC-SubnetB - - !ImportValue VPC-SubnetC - - !ImportValue VPC-SubnetD - - !ImportValue VPC-SubnetE + Subnets: !Ref PrivateSubnets LoadBalancers: - ContainerName: aiproxy ContainerPort: 80 @@ -163,7 +163,7 @@ Resources: Properties: GroupDescription: Security Group for ECS Service # TODO: This copies geocoder, but we should probably have a separate VPC for this service. - VpcId: !ImportValue VPC + VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 @@ -177,7 +177,7 @@ Resources: RequiresCompatibilities: - FARGATE NetworkMode: awsvpc - ExecutionRoleArn: !ImportValue AiProxyECSTaskExecutionRoleArn + ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn Cpu: 256 Memory: 512 ContainerDefinitions: @@ -195,12 +195,50 @@ Resources: awslogs-region: !Ref AWS::Region awslogs-stream-prefix: ecs + ECSTaskExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - ecs-tasks.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: ECRPolicy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - ecr:GetAuthorizationToken + - ecr:BatchCheckLayerAvailability + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + Resource: !Ref ECRRepositoryArn + - PolicyName: LogsPolicy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: + - !GetAtt LogGroup.Arn + - !Sub "${LogGroup.Arn}:*" + # ------------------ # Logging & Alerts # ------------------ LogGroup: Type: AWS::Logs::LogGroup + DeletionPolicy: Retain Properties: LogGroupName: !Sub "${AWS::StackName}" From 2bd3f138a6bd3d71aa89e2a3dc97d6446cb53dc7 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:21:51 -0600 Subject: [PATCH 07/20] rearrange and add comments --- cicd/2-cicd/cicd.template.yml | 76 ++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/cicd/2-cicd/cicd.template.yml b/cicd/2-cicd/cicd.template.yml index b97f5f9..c66b5a9 100644 --- a/cicd/2-cicd/cicd.template.yml +++ b/cicd/2-cicd/cicd.template.yml @@ -40,8 +40,11 @@ Conditions: Resources: - # The Elastic Container Registry Repository will store our built docker - # images. + #------------------------------------- + # Elastic Container Registry (ECR) + # - Store built docker images + #------------------------------------- + EcrRepository: Type: AWS::ECR::Repository Properties: @@ -113,6 +116,10 @@ Resources: - 'kms:GenerateDataKeyWithoutPlaintext' Resource: '*' + #------------------------------------- + # CodeBuild Projects + #------------------------------------- + # The CodeBuild Project is triggered by pull requests targeting $GitHubBranch # It will perform any steps defined in the pr-buildspec.yml file. PullRequestBuildProject: @@ -191,32 +198,9 @@ Resources: Artifacts: Type: CODEPIPELINE - # Grant the AiProxy CodeBuild Role additional permissions for resources in - # this template. This allows us to avoid granting permission to * resources. - AiProxyRolePolicy: - Type: 'AWS::IAM::Policy' - Properties: - PolicyName: !Sub "${AWS::StackName}-codebuild-policy" - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - codebuild:* - Resource: - - !GetAtt AppBuildProject.Arn - - !GetAtt IntegrationTestBuildProject.Arn - - Effect: Allow - Action: - - codebuild:CreateReportGroup - - codebuild:CreateReport - - codebuild:UpdateReport - - codebuild:BatchPutTestCases - - codebuild:BatchPutCodeCoverage - Resource: - - !Sub arn:aws:codebuild:us-east-1:165336972514:report-group/${AWS::StackName}-${GitHubBranch}-pr-build - Roles: - - !ImportValue AiProxyCodeBuildRoleName + #------------------------------------- + # Pipeline + #------------------------------------- Pipeline: Type: AWS::CodePipeline::Pipeline @@ -430,6 +414,42 @@ Resources: - Name: smokeTestResults - !Ref AWS::NoValue + + #------------------------------------- + # IAM Roles & Policies + #------------------------------------- + + # Grant the AiProxy CodeBuild Role additional permissions for resources in + # this template. This allows us to avoid granting permission to * resources. + AiProxyRolePolicy: + Type: 'AWS::IAM::Policy' + Properties: + PolicyName: !Sub "${AWS::StackName}-codebuild-policy" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - codebuild:* + Resource: + - !GetAtt AppBuildProject.Arn + - !GetAtt IntegrationTestBuildProject.Arn + - Effect: Allow + Action: + - codebuild:CreateReportGroup + - codebuild:CreateReport + - codebuild:UpdateReport + - codebuild:BatchPutTestCases + - codebuild:BatchPutCodeCoverage + Resource: + - !Sub arn:aws:codebuild:us-east-1:165336972514:report-group/${AWS::StackName}-${GitHubBranch}-pr-build + Roles: + - !ImportValue AiProxyCodeBuildRoleName + + #------------------------------------- + # Metrics & Notifications + #------------------------------------- + # Send pipeline events to an SNS topic. # Note: # Integration with Slack via AWS ChatBot is configured manually via AWS From 4b513a63177f0cff2ede0995b4f587b3a26b51bc Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:29:56 -0600 Subject: [PATCH 08/20] remove migrated role --- cicd/1-setup/cicd-dependencies.template.yml | 43 --------------------- 1 file changed, 43 deletions(-) diff --git a/cicd/1-setup/cicd-dependencies.template.yml b/cicd/1-setup/cicd-dependencies.template.yml index 78f08ed..25e586d 100644 --- a/cicd/1-setup/cicd-dependencies.template.yml +++ b/cicd/1-setup/cicd-dependencies.template.yml @@ -112,45 +112,6 @@ Resources: - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:cicd/docker-hub/username-ITBwTx - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:cicd/docker-hub/read-only-token-SsbSj7 - # This would ideally be defined in 3-app/aiproxy/template.yml, but we do - # not allow the CloudFormationServiceRole permission to CreateRole, so we - # have to define it here. TODO: Tweak permissions for the role used to create - # the cloudformation stack. - ECSTaskExecutionRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ecs-tasks.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: ECRPolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ecr:GetAuthorizationToken - - ecr:BatchCheckLayerAvailability - - ecr:GetDownloadUrlForLayer - - ecr:BatchGetImage - Resource: "*" - - PolicyName: LogsPolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: "*" - Outputs: CodeBuildArtifactBucket: Description: AiProxy CodeBuild Artifact Bucket Name @@ -172,7 +133,3 @@ Outputs: Description: AiProxy Pull Request CodeBuild Role Name Value: !Ref PublicCodeBuildRole Export: {Name: AiProxyPublicCodeBuildRoleName} - ECSTaskExecutionRoleArn: - Description: AiProxy ECS Task Execution Role ARN - Value: !GetAtt ECSTaskExecutionRole.Arn - Export: {Name: AiProxyECSTaskExecutionRoleArn} From 477bdc310b42dcb9e4a4718d4e553cd06fd02942 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 00:31:02 -0600 Subject: [PATCH 09/20] keep old role to avoid breaking active stacks --- cicd/1-setup/cicd-dependencies.template.yml | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/cicd/1-setup/cicd-dependencies.template.yml b/cicd/1-setup/cicd-dependencies.template.yml index 25e586d..78f08ed 100644 --- a/cicd/1-setup/cicd-dependencies.template.yml +++ b/cicd/1-setup/cicd-dependencies.template.yml @@ -112,6 +112,45 @@ Resources: - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:cicd/docker-hub/username-ITBwTx - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:cicd/docker-hub/read-only-token-SsbSj7 + # This would ideally be defined in 3-app/aiproxy/template.yml, but we do + # not allow the CloudFormationServiceRole permission to CreateRole, so we + # have to define it here. TODO: Tweak permissions for the role used to create + # the cloudformation stack. + ECSTaskExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - ecs-tasks.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: ECRPolicy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - ecr:GetAuthorizationToken + - ecr:BatchCheckLayerAvailability + - ecr:GetDownloadUrlForLayer + - ecr:BatchGetImage + Resource: "*" + - PolicyName: LogsPolicy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: "*" + Outputs: CodeBuildArtifactBucket: Description: AiProxy CodeBuild Artifact Bucket Name @@ -133,3 +172,7 @@ Outputs: Description: AiProxy Pull Request CodeBuild Role Name Value: !Ref PublicCodeBuildRole Export: {Name: AiProxyPublicCodeBuildRoleName} + ECSTaskExecutionRoleArn: + Description: AiProxy ECS Task Execution Role ARN + Value: !GetAtt ECSTaskExecutionRole.Arn + Export: {Name: AiProxyECSTaskExecutionRoleArn} From 8fa15045f3865146a283ed5ccd83a51253ca179a Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 10:51:16 -0600 Subject: [PATCH 10/20] support stack creation --- cicd/1-setup/deploy-cicd-dependencies.sh | 6 ++++++ cicd/2-cicd/deploy-cicd.sh | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/cicd/1-setup/deploy-cicd-dependencies.sh b/cicd/1-setup/deploy-cicd-dependencies.sh index babf20b..0cefdb9 100755 --- a/cicd/1-setup/deploy-cicd-dependencies.sh +++ b/cicd/1-setup/deploy-cicd-dependencies.sh @@ -19,11 +19,17 @@ REGION=$(aws configure get region) read -r -p "Would you like to create a change set for this template in AWS account $ACCOUNT? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] then + CHANGE_SET_TYPE="UPDATE" + if ! aws cloudformation describe-stacks --stack-name aiproxy-cicd-deps > /dev/null 2>&1; then + CHANGE_SET_TYPE="CREATE" + fi + echo Creating change set... CHANGE_SET_NAME="aiproxy-cicd-deps-changeset-$(date +%s)" aws cloudformation create-change-set \ --stack-name aiproxy-cicd-deps \ --change-set-name $CHANGE_SET_NAME \ + --change-set-type $CHANGE_SET_TYPE \ --template-body file://${TEMPLATE_FILE} \ --capabilities CAPABILITY_IAM \ --tags EnvType=infrastructure \ diff --git a/cicd/2-cicd/deploy-cicd.sh b/cicd/2-cicd/deploy-cicd.sh index 9669a4d..2c56a3e 100755 --- a/cicd/2-cicd/deploy-cicd.sh +++ b/cicd/2-cicd/deploy-cicd.sh @@ -45,14 +45,22 @@ aws cloudformation validate-template \ ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) REGION=$(aws configure get region) +echo "Stack name is: $STACK_NAME" + read -r -p "Would you like to create a change set for this template in AWS account $ACCOUNT? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] then + CHANGE_SET_TYPE="UPDATE" + if ! aws cloudformation describe-stacks --stack-name $STACK_NAME > /dev/null 2>&1; then + CHANGE_SET_TYPE="CREATE" + fi + echo Creating change set... CHANGE_SET_NAME="${STACK_NAME}-changeset-$(date +%s)" aws cloudformation create-change-set \ --stack-name $STACK_NAME \ --change-set-name $CHANGE_SET_NAME \ + --change-set-type $CHANGE_SET_TYPE \ --template-body file://${TEMPLATE_FILE} \ --parameters ParameterKey=GitHubBranch,ParameterValue=$TARGET_BRANCH ParameterKey=GitHubBadgeEnabled,ParameterValue=$GITHUB_BADGE_ENABLED ParameterKey=EnvironmentType,ParameterValue=$ENVIRONMENT_TYPE \ --capabilities CAPABILITY_IAM \ @@ -61,6 +69,9 @@ then echo "Change set created. You can review it at:" echo "https://console.aws.amazon.com/cloudformation/home?region=$REGION#/stacks/changesets/changes?stackId=arn:aws:cloudformation:$REGION:$ACCOUNT:stack/$STACK_NAME/*&changeSetId=arn:aws:cloudformation:$REGION:$ACCOUNT:changeSet/$CHANGE_SET_NAME" + # TODO: That's wrong, it's at https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/changesets/changes?stackId=arn%3Aaws%3Acloudformation%3Aus-east-1%3A475661607190%3Astack%2Faiproxy-refactor-iam-role-creation-cicd%2F90acf300-b169-11ee-8df0-0a6d63bbc467&changeSetId=arn%3Aaws%3Acloudformation%3Aus-east-1%3A475661607190%3AchangeSet%2Faiproxy-refactor-iam-role-creation-cicd-changeset-1705077744%2F9b26634f-00f8-4e37-954f-b7c8877f46d1 + + read -r -p "Would you like to execute the change set? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] From 3ae221f87dbeef666caee4a838e561e9f25b4de9 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 10:51:32 -0600 Subject: [PATCH 11/20] fix !Sub and !ImportValue syntax --- cicd/2-cicd/cicd.template.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cicd/2-cicd/cicd.template.yml b/cicd/2-cicd/cicd.template.yml index c66b5a9..3e1ac2d 100644 --- a/cicd/2-cicd/cicd.template.yml +++ b/cicd/2-cicd/cicd.template.yml @@ -275,11 +275,11 @@ Resources: - '"SecurityGroup": "${SecurityGroupValue}",' - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } - !Sub - - '"PublicSubnets": ${PublicSubnetsValue},' - - { PublicSubnetsValue: [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE] } + - '"PublicSubnets": "[${PublicSubnetsValue}]",' + - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": ${PrivateSubnetsValue},' - - { PrivateSubnetsValue: [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE] } + - '"PrivateSubnets": "[${PrivateSubnetsValue}]",' + - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/admin/CloudFormationService @@ -317,11 +317,11 @@ Resources: - '"SecurityGroup": "${SecurityGroupValue}",' - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } - !Sub - - '"PublicSubnets": ${PublicSubnetsValue},' - - { PublicSubnetsValue: [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE] } + - '"PublicSubnets": "[${PublicSubnetsValue}]",' + - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": ${PrivateSubnetsValue},' - - { PrivateSubnetsValue: [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE] } + - '"PrivateSubnets": "[${PrivateSubnetsValue}]",' + - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/admin/CloudFormationService @@ -382,11 +382,11 @@ Resources: - '"SecurityGroup": "${SecurityGroupValue}",' - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } - !Sub - - '"PublicSubnets": ${PublicSubnetsValue},' - - { PublicSubnetsValue: [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE] } + - '"PublicSubnets": "[${PublicSubnetsValue}]",' + - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": ${PrivateSubnetsValue},' - - { PrivateSubnetsValue: [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE] } + - '"PrivateSubnets": "[${PrivateSubnetsValue}]",' + - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/admin/CloudFormationService From edbdbc7bd348d2ab6350b0a1eaa93a4c84884ef5 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 12:49:51 -0600 Subject: [PATCH 12/20] revert switch to create-change-set --- cicd/1-setup/deploy-cicd-dependencies.sh | 37 +++---------- cicd/2-cicd/deploy-cicd.sh | 70 +++++++----------------- 2 files changed, 29 insertions(+), 78 deletions(-) diff --git a/cicd/1-setup/deploy-cicd-dependencies.sh b/cicd/1-setup/deploy-cicd-dependencies.sh index 0cefdb9..735e57f 100755 --- a/cicd/1-setup/deploy-cicd-dependencies.sh +++ b/cicd/1-setup/deploy-cicd-dependencies.sh @@ -11,44 +11,23 @@ TEMPLATE_FILE=cicd/1-setup/cicd-dependencies.template.yml echo Validating cloudformation template... aws cloudformation validate-template \ --template-body file://${TEMPLATE_FILE} \ - > /dev/null + | cat ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) -REGION=$(aws configure get region) -read -r -p "Would you like to create a change set for this template in AWS account $ACCOUNT? [y/N] " response +read -r -p "Would you like to deploy this template to AWS account $ACCOUNT? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] then - CHANGE_SET_TYPE="UPDATE" - if ! aws cloudformation describe-stacks --stack-name aiproxy-cicd-deps > /dev/null 2>&1; then - CHANGE_SET_TYPE="CREATE" - fi - - echo Creating change set... - CHANGE_SET_NAME="aiproxy-cicd-deps-changeset-$(date +%s)" - aws cloudformation create-change-set \ + echo Updating cloudformation stack... + echo "Follow along at https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks?filteringText=aiproxy-cicd-deps&filteringStatus=active&viewNested=true&hideStacks=false" + aws cloudformation deploy \ --stack-name aiproxy-cicd-deps \ - --change-set-name $CHANGE_SET_NAME \ - --change-set-type $CHANGE_SET_TYPE \ - --template-body file://${TEMPLATE_FILE} \ + --template-file ${TEMPLATE_FILE} \ --capabilities CAPABILITY_IAM \ --tags EnvType=infrastructure \ "$@" - echo "Change set created. You can review it at:" - echo "https://console.aws.amazon.com/cloudformation/home?region=$REGION#/stacks/changesets/changes?stackId=arn:aws:cloudformation:$REGION:$ACCOUNT:stack/aiproxy-cicd-deps/*&changeSetId=arn:aws:cloudformation:$REGION:$ACCOUNT:changeSet/$CHANGE_SET_NAME" - - read -r -p "Would you like to execute the change set? [y/N] " response - if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] - then - echo Executing change set... - aws cloudformation execute-change-set \ - --stack-name aiproxy-cicd-deps \ - --change-set-name $CHANGE_SET_NAME - echo Complete! - else - echo Exiting... - fi + echo Complete! else echo Exiting... -fi \ No newline at end of file +fi diff --git a/cicd/2-cicd/deploy-cicd.sh b/cicd/2-cicd/deploy-cicd.sh index 2c56a3e..357dca8 100755 --- a/cicd/2-cicd/deploy-cicd.sh +++ b/cicd/2-cicd/deploy-cicd.sh @@ -8,82 +8,54 @@ echo Deploying AiProxy CICD Pipeline # - ENVIRONMENT_TYPE: Can be 'production' (default) or 'development', passed as a Parameter for "cicd/2-cicd/cicd.template.yml" # - GITHUB_BADGE_ENABLED: defaults to true, passed as a Parameter for "cicd/2-cicd/cicd.template.yml" -# The branch name may become part of a domain name, so we need to validate it. -validate_branch_name() { - local branch_name=$1 - if [[ ! $branch_name =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])$ ]]; then - echo "Invalid branch name '${branch_name}', branches must be alphanumeric and may contain hyphens." - exit 1 - fi -} - # 'Developer' role requires a specific service role for all CloudFormation operations. if [[ $(aws sts get-caller-identity --query Arn --output text) =~ "assumed-role/Developer/" ]]; then # Append the role-arn option to the positional parameters $@ passed to cloudformation deploy. set -- "$@" --role-arn "arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/admin/CloudFormationService" fi -ENVIRONMENT_TYPE=${ENVIRONMENT_TYPE:-'production'} -GITHUB_BADGE_ENABLED=${GITHUB_BADGE_ENABLED:-'true'} -TARGET_BRANCH=${TARGET_BRANCH:-'main'} +# Default to main branch, but support pipelines using other branches +TARGET_BRANCH=${TARGET_BRANCH-'main'} if [ "$TARGET_BRANCH" == "main" ] then STACK_NAME="aiproxy-cicd" else - validate_branch_name "$TARGET_BRANCH" - STACK_NAME="aiproxy-${TARGET_BRANCH}-cicd" + # only allow alphanumeric branch names that may contain an internal hyphen. + # to avoid complicated logic elsewhere, we're constraining it here. + if [[ "$TARGET_BRANCH" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])$ ]]; then + STACK_NAME="aiproxy-${TARGET_BRANCH}-cicd" + else + echo "Invalid branch name '${TARGET_BRANCH}', branches must be alphanumeric and may contain hyphens." + exit + fi fi +ENVIRONMENT_TYPE=${ENVIRONMENT_TYPE-'production'} +GITHUB_BADGE_ENABLED=${GITHUB_BADGE_ENABLED-'true'} + TEMPLATE_FILE=cicd/2-cicd/cicd.template.yml echo Validating cloudformation template... aws cloudformation validate-template \ --template-body file://${TEMPLATE_FILE} \ - > /dev/null + | cat ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) -REGION=$(aws configure get region) - -echo "Stack name is: $STACK_NAME" -read -r -p "Would you like to create a change set for this template in AWS account $ACCOUNT? [y/N] " response +read -r -p "Would you like to deploy this template to AWS account $ACCOUNT? [y/N] " response if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] then - CHANGE_SET_TYPE="UPDATE" - if ! aws cloudformation describe-stacks --stack-name $STACK_NAME > /dev/null 2>&1; then - CHANGE_SET_TYPE="CREATE" - fi - - echo Creating change set... - CHANGE_SET_NAME="${STACK_NAME}-changeset-$(date +%s)" - aws cloudformation create-change-set \ + echo Updating cloudformation stack... + aws cloudformation deploy \ --stack-name $STACK_NAME \ - --change-set-name $CHANGE_SET_NAME \ - --change-set-type $CHANGE_SET_TYPE \ - --template-body file://${TEMPLATE_FILE} \ - --parameters ParameterKey=GitHubBranch,ParameterValue=$TARGET_BRANCH ParameterKey=GitHubBadgeEnabled,ParameterValue=$GITHUB_BADGE_ENABLED ParameterKey=EnvironmentType,ParameterValue=$ENVIRONMENT_TYPE \ + --template-file $TEMPLATE_FILE \ + --parameter-overrides GitHubBranch=$TARGET_BRANCH GitHubBadgeEnabled=$GITHUB_BADGE_ENABLED EnvironmentType=$ENVIRONMENT_TYPE \ --capabilities CAPABILITY_IAM \ - --tags Key=EnvType,Value=${ENVIRONMENT_TYPE} \ + --tags EnvType=${ENVIRONMENT_TYPE} \ "$@" - echo "Change set created. You can review it at:" - echo "https://console.aws.amazon.com/cloudformation/home?region=$REGION#/stacks/changesets/changes?stackId=arn:aws:cloudformation:$REGION:$ACCOUNT:stack/$STACK_NAME/*&changeSetId=arn:aws:cloudformation:$REGION:$ACCOUNT:changeSet/$CHANGE_SET_NAME" - # TODO: That's wrong, it's at https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/changesets/changes?stackId=arn%3Aaws%3Acloudformation%3Aus-east-1%3A475661607190%3Astack%2Faiproxy-refactor-iam-role-creation-cicd%2F90acf300-b169-11ee-8df0-0a6d63bbc467&changeSetId=arn%3Aaws%3Acloudformation%3Aus-east-1%3A475661607190%3AchangeSet%2Faiproxy-refactor-iam-role-creation-cicd-changeset-1705077744%2F9b26634f-00f8-4e37-954f-b7c8877f46d1 - - - - read -r -p "Would you like to execute the change set? [y/N] " response - if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]] - then - echo Executing change set... - aws cloudformation execute-change-set \ - --stack-name $STACK_NAME \ - --change-set-name $CHANGE_SET_NAME - echo Complete! - else - echo Exiting... - fi + echo Complete! else echo Exiting... fi From 9f3b3b56a7ea053b95f06541a15c85d432400011 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 12:54:29 -0600 Subject: [PATCH 13/20] fix template ref typo --- cicd/3-app/aiproxy/template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cicd/3-app/aiproxy/template.yml b/cicd/3-app/aiproxy/template.yml index c0ccc61..768e169 100644 --- a/cicd/3-app/aiproxy/template.yml +++ b/cicd/3-app/aiproxy/template.yml @@ -72,7 +72,7 @@ Resources: LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: 180 - SecurityGroups: !Ref LoadBalancerSecurityGroups + SecurityGroups: [!Ref SecurityGroup] # Place load balancer in public subnets, so it's accessible from the internet. # We may want to move this to the private subnets, so only internal resources # can access it, but this is very convenient for local development. From 9f40ff9e613f582c21850ca8b743d792337b5ac5 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 12:54:52 -0600 Subject: [PATCH 14/20] ensure logs aren't destroyed --- cicd/3-app/aiproxy/template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/cicd/3-app/aiproxy/template.yml b/cicd/3-app/aiproxy/template.yml index 768e169..635bdab 100644 --- a/cicd/3-app/aiproxy/template.yml +++ b/cicd/3-app/aiproxy/template.yml @@ -239,6 +239,7 @@ Resources: LogGroup: Type: AWS::Logs::LogGroup DeletionPolicy: Retain + UpdateReplacePolicy: Retain Properties: LogGroupName: !Sub "${AWS::StackName}" From 3e123f975694a6202ef8b2f9fa327d2450f1203d Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 12:55:21 -0600 Subject: [PATCH 15/20] silence useless output --- cicd/1-setup/deploy-cicd-dependencies.sh | 2 +- cicd/2-cicd/deploy-cicd.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cicd/1-setup/deploy-cicd-dependencies.sh b/cicd/1-setup/deploy-cicd-dependencies.sh index 735e57f..d02e358 100755 --- a/cicd/1-setup/deploy-cicd-dependencies.sh +++ b/cicd/1-setup/deploy-cicd-dependencies.sh @@ -11,7 +11,7 @@ TEMPLATE_FILE=cicd/1-setup/cicd-dependencies.template.yml echo Validating cloudformation template... aws cloudformation validate-template \ --template-body file://${TEMPLATE_FILE} \ - | cat + > /dev/null ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) diff --git a/cicd/2-cicd/deploy-cicd.sh b/cicd/2-cicd/deploy-cicd.sh index 357dca8..31e87c7 100755 --- a/cicd/2-cicd/deploy-cicd.sh +++ b/cicd/2-cicd/deploy-cicd.sh @@ -39,7 +39,7 @@ TEMPLATE_FILE=cicd/2-cicd/cicd.template.yml echo Validating cloudformation template... aws cloudformation validate-template \ --template-body file://${TEMPLATE_FILE} \ - | cat + > /dev/null ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) From 9b7d879110306f8167cff02ee5a8c0ad2a4d7df1 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 15:56:40 -0600 Subject: [PATCH 16/20] remove trailing commas --- cicd/2-cicd/cicd.template.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cicd/2-cicd/cicd.template.yml b/cicd/2-cicd/cicd.template.yml index 3e1ac2d..d28dc66 100644 --- a/cicd/2-cicd/cicd.template.yml +++ b/cicd/2-cicd/cicd.template.yml @@ -278,7 +278,7 @@ Resources: - '"PublicSubnets": "[${PublicSubnetsValue}]",' - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": "[${PrivateSubnetsValue}]",' + - '"PrivateSubnets": "[${PrivateSubnetsValue}]"' - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM @@ -320,7 +320,7 @@ Resources: - '"PublicSubnets": "[${PublicSubnetsValue}]",' - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": "[${PrivateSubnetsValue}]",' + - '"PrivateSubnets": "[${PrivateSubnetsValue}]"' - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM @@ -385,7 +385,7 @@ Resources: - '"PublicSubnets": "[${PublicSubnetsValue}]",' - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": "[${PrivateSubnetsValue}]",' + - '"PrivateSubnets": "[${PrivateSubnetsValue}]"' - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM From 69aa26072305cc60f0952aae7d1a7e4054263fe5 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 16:36:18 -0600 Subject: [PATCH 17/20] tweak array syntax --- cicd/2-cicd/cicd.template.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cicd/2-cicd/cicd.template.yml b/cicd/2-cicd/cicd.template.yml index d28dc66..f668408 100644 --- a/cicd/2-cicd/cicd.template.yml +++ b/cicd/2-cicd/cicd.template.yml @@ -275,10 +275,10 @@ Resources: - '"SecurityGroup": "${SecurityGroupValue}",' - { SecurityGroupValue: !ImportValue VPC-ELBSecurityGroup } - !Sub - - '"PublicSubnets": "[${PublicSubnetsValue}]",' + - '"PublicSubnets": "${PublicSubnetsValue}",' - { PublicSubnetsValue: !Join [",", [!ImportValue VPC-PublicSubnetB, !ImportValue VPC-PublicSubnetC, !ImportValue VPC-PublicSubnetD, !ImportValue VPC-PublicSubnetE]] } - !Sub - - '"PrivateSubnets": "[${PrivateSubnetsValue}]"' + - '"PrivateSubnets": "${PrivateSubnetsValue}"' - { PrivateSubnetsValue: !Join [",", [!ImportValue VPC-SubnetB, !ImportValue VPC-SubnetC, !ImportValue VPC-SubnetD, !ImportValue VPC-SubnetE]] } - '}' Capabilities: CAPABILITY_AUTO_EXPAND,CAPABILITY_IAM From 48539739a31ced3b7e2933407e29b9ea44357a60 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 16:44:02 -0600 Subject: [PATCH 18/20] import ecs task execution role --- cicd/1-setup/cicd-dependencies.template.yml | 1 + cicd/3-app/aiproxy/template.yml | 74 ++++++++++----------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/cicd/1-setup/cicd-dependencies.template.yml b/cicd/1-setup/cicd-dependencies.template.yml index 78f08ed..69420b9 100644 --- a/cicd/1-setup/cicd-dependencies.template.yml +++ b/cicd/1-setup/cicd-dependencies.template.yml @@ -40,6 +40,7 @@ Resources: - Effect: Allow Action: - "cloudformation:DescribeStacks" + - "cloudformation:DescribeStackEvents" - "cloudformation:CreateStack" - "cloudformation:UpdateStack" Resource: "*" # TODO scope to specific stacks diff --git a/cicd/3-app/aiproxy/template.yml b/cicd/3-app/aiproxy/template.yml index 635bdab..fd09590 100644 --- a/cicd/3-app/aiproxy/template.yml +++ b/cicd/3-app/aiproxy/template.yml @@ -177,7 +177,7 @@ Resources: RequiresCompatibilities: - FARGATE NetworkMode: awsvpc - ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn + ExecutionRoleArn: !ImportValue AiProxyECSTaskExecutionRoleArn Cpu: 256 Memory: 512 ContainerDefinitions: @@ -195,42 +195,42 @@ Resources: awslogs-region: !Ref AWS::Region awslogs-stream-prefix: ecs - ECSTaskExecutionRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Principal: - Service: - - ecs-tasks.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: ECRPolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - ecr:GetAuthorizationToken - - ecr:BatchCheckLayerAvailability - - ecr:GetDownloadUrlForLayer - - ecr:BatchGetImage - Resource: !Ref ECRRepositoryArn - - PolicyName: LogsPolicy - PolicyDocument: - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !GetAtt LogGroup.Arn - - !Sub "${LogGroup.Arn}:*" + # ECSTaskExecutionRole: + # Type: AWS::IAM::Role + # Properties: + # AssumeRolePolicyDocument: + # Version: "2012-10-17" + # Statement: + # - Effect: Allow + # Principal: + # Service: + # - ecs-tasks.amazonaws.com + # Action: + # - sts:AssumeRole + # Policies: + # - PolicyName: ECRPolicy + # PolicyDocument: + # Version: "2012-10-17" + # Statement: + # - Effect: Allow + # Action: + # - ecr:GetAuthorizationToken + # - ecr:BatchCheckLayerAvailability + # - ecr:GetDownloadUrlForLayer + # - ecr:BatchGetImage + # Resource: !Ref ECRRepositoryArn + # - PolicyName: LogsPolicy + # PolicyDocument: + # Version: "2012-10-17" + # Statement: + # - Effect: Allow + # Action: + # - logs:CreateLogGroup + # - logs:CreateLogStream + # - logs:PutLogEvents + # Resource: + # - !GetAtt LogGroup.Arn + # - !Sub "${LogGroup.Arn}:*" # ------------------ # Logging & Alerts From 1c50a8ffaae7aa5d7e7ebc846eabeb74ac2c2c56 Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 16:57:50 -0600 Subject: [PATCH 19/20] comment unused parameter --- cicd/3-app/aiproxy/template.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cicd/3-app/aiproxy/template.yml b/cicd/3-app/aiproxy/template.yml index fd09590..81fc1ab 100644 --- a/cicd/3-app/aiproxy/template.yml +++ b/cicd/3-app/aiproxy/template.yml @@ -12,9 +12,9 @@ Parameters: SubdomainName: Type: String Description: Subdomain name for aiproxy service (e.g. 'aiproxy' in 'aiproxy.code.org'). - ECRRepositoryArn: - Type: String - Description: ARN of the ECR repository for this service. + # ECRRepositoryArn: + # Type: String + # Description: ARN of the ECR repository for this service. AppImageUri: Type: String Description: URI of the Docker image in ECR. From e9fdbd33111504004db732708003537e349d71ca Mon Sep 17 00:00:00 2001 From: Darin Webb Date: Fri, 12 Jan 2024 17:08:33 -0600 Subject: [PATCH 20/20] comment out unused parameter --- cicd/2-cicd/cicd.template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cicd/2-cicd/cicd.template.yml b/cicd/2-cicd/cicd.template.yml index f668408..eb800d9 100644 --- a/cicd/2-cicd/cicd.template.yml +++ b/cicd/2-cicd/cicd.template.yml @@ -267,7 +267,7 @@ Resources: - '' - - '{' - !Sub '"SubdomainName": "aiproxy-dev-${GitHubBranch}",' - - !Sub '"ECRRepositoryArn": "${EcrRepository.Arn}",' + # - !Sub '"ECRRepositoryArn": "${EcrRepository.Arn}",' - !Sub - '"VPC": "${VPCValue}",' - { VPCValue: !ImportValue VPC }