diff --git a/ci/terraform/oidc/sandpit.tfvars b/ci/terraform/oidc/sandpit.tfvars index 3065bfd271..9b6c1e0aec 100644 --- a/ci/terraform/oidc/sandpit.tfvars +++ b/ci/terraform/oidc/sandpit.tfvars @@ -65,6 +65,7 @@ orch_account_id = "816047645251" back_channel_logout_cross_account_access_enabled = true kms_cross_account_access_enabled = true cmk_for_back_channel_logout_enabled = true +spot_request_queue_cross_account_access_enabled = true oidc_origin_domain_enabled = true diff --git a/ci/terraform/oidc/spot-sqs.tf b/ci/terraform/oidc/spot-sqs.tf index e3d17937c3..28a211d141 100644 --- a/ci/terraform/oidc/spot-sqs.tf +++ b/ci/terraform/oidc/spot-sqs.tf @@ -27,6 +27,48 @@ resource "aws_sqs_queue" "spot_request_dead_letter_queue" { tags = local.default_tags } +data "aws_iam_policy_document" "cross_account_spot_request_queue_policy_document" { + statement { + sid = "AllowSpotAccountToReceive" + effect = "Allow" + + principals { + type = "AWS" + identifiers = [aws_ssm_parameter.spot_account_number.value] + } + + actions = [ + "sqs:ReceiveMessage", + "sqs:ChangeMessageVisibility", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ] + + resources = [ + aws_sqs_queue.spot_request_queue.arn + ] + } + + statement { + sid = "AllowOrchAccountSendSQS-${var.environment}" + effect = "Allow" + + principals { + type = "AWS" + identifiers = [var.orch_account_id] + } + + actions = [ + "sqs:SendMessage", + "sqs:ChangeMessageVisibility", + "sqs:GetQueueAttributes", + ] + resources = [ + aws_sqs_queue.spot_request_queue.arn + ] + } +} + data "aws_iam_policy_document" "spot_request_queue_policy_document" { statement { sid = "AllowSpotAccountToReceive" @@ -51,12 +93,8 @@ data "aws_iam_policy_document" "spot_request_queue_policy_document" { } resource "aws_sqs_queue_policy" "spot_request_queue_policy" { - depends_on = [ - data.aws_iam_policy_document.spot_request_queue_policy_document, - ] - queue_url = aws_sqs_queue.spot_request_queue.id - policy = data.aws_iam_policy_document.spot_request_queue_policy_document.json + policy = var.spot_request_queue_cross_account_access_enabled ? data.aws_iam_policy_document.cross_account_spot_request_queue_policy_document.json : data.aws_iam_policy_document.spot_request_queue_policy_document.json } data "aws_iam_policy_document" "spot_request_dlq_queue_policy_document" { @@ -119,10 +157,56 @@ data "aws_iam_policy_document" "spot_request_kms_key_policy" { } } +data "aws_iam_policy_document" "cross_account_spot_request_kms_key_policy" { + policy_id = "cross-account-key-policy-ssm" + + statement { + sid = "Enable IAM User Permissions for root user" + actions = [ + "kms:*", + ] + effect = "Allow" + principals { + type = "AWS" + identifiers = [data.aws_caller_identity.current.account_id] + } + resources = ["*"] + } + + statement { + sid = "Give SPOT permissions to SQS KMS key" + actions = [ + "kms:Decrypt", + ] + effect = "Allow" + principals { + type = "AWS" + identifiers = [aws_ssm_parameter.spot_account_number.value] + } + resources = ["*"] + } + + statement { + sid = "AllowOrchAccessToSpotRequestQueueEncryptionKey-${var.environment}" + effect = "Allow" + + actions = [ + "kms:Decrypt", + "kms:GenerateDataKey" + ] + resources = ["*"] + + principals { + type = "AWS" + identifiers = [var.orch_account_id] + } + } +} + resource "aws_kms_key" "spot_request_sqs_key" { description = "KMS key for SPOT request SQS queue encryption" deletion_window_in_days = 30 - policy = data.aws_iam_policy_document.spot_request_kms_key_policy.json + policy = var.spot_request_queue_cross_account_access_enabled ? data.aws_iam_policy_document.cross_account_spot_request_kms_key_policy.json : data.aws_iam_policy_document.spot_request_kms_key_policy.json customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" diff --git a/ci/terraform/oidc/variables.tf b/ci/terraform/oidc/variables.tf index 40e313fe6c..b785b8a50b 100644 --- a/ci/terraform/oidc/variables.tf +++ b/ci/terraform/oidc/variables.tf @@ -599,6 +599,12 @@ variable "kms_cross_account_access_enabled" { description = "Whether the service should allow cross-account access by the orchestration account to kms" } +variable "spot_request_queue_cross_account_access_enabled" { + default = false + type = bool + description = "Whether the service should allow cross-account access by orchestration to the SPoT request queue" +} + variable "oidc_origin_domain_enabled" { type = bool default = false diff --git a/test-template.yaml b/test-template.yaml new file mode 100644 index 0000000000..1955509801 --- /dev/null +++ b/test-template.yaml @@ -0,0 +1,6615 @@ +AWSTemplateFormatVersion: "2010-09-09" + +Description: > + devplatform-deploy sam-deploy-pipeline template version: v2.52.4 + TEST TEMPLATE, DO NOT DEPLOY BEYOND DEV. + + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "General configuration" + Parameters: + - SAMStackName + - Environment + - VpcStackName + - RequireManualApproval + - IncludePromotion + - AllowedAccounts + - AWSOrganizationId + - LogRetentionDays + - InvokableLambdaAccounts + - AccessDynamoDBAccounts + - AbortOnCheckovFailure + - Label: + default: "Signing configuration" + Parameters: + - ContainerSignerKmsKeyArn + - CustomKmsKeyArns + - SigningProfileArn + - SigningProfileVersionArn + - AdditionalCodeSigningVersionArns + - Label: + default: "Source configuration" + Parameters: + - ArtifactSourceBucketArn + - ArtifactSourceBucketEventTriggerRoleArn + - GitHubRepositoryName + - OneLoginRepositoryName + - Label: + default: "Permissions Boundary Configuration" + Parameters: + - ProgrammaticPermissionsBoundary + - AllowedServiceOne + - AllowedServiceTwo + - AllowedServiceThree + - AllowedServiceFour + - AllowedServiceFive + - AllowedServiceSix + - AllowedServiceSeven + - DynamicResourcesPrefix + - Label: + default: "Test configuration" + Parameters: + - RequireTestContainerSignatureValidation + - TestImageRepositoryUri + - TestReportFormat + - TestCoverageReportFormat + - TestComputeType + - RunTestContainerInVPC + - Label: + default: "Canary Deployments" + Parameters: + - ECSCanaryDeployment + - LambdaCanaryDeployment + - TrafficTestImageRepositoryUri + - Label: + default: "Build notifications" + Parameters: + - SlackNotificationType + - BuildNotificationStackName + - Label: + default: "Log artifact write times to Grafana" + Parameters: + - ArtifactWriteTimeLoggingLambdaArn + - Label: + default: "Bucket configurations" + Parameters: + - AccessLogsCustomBucketNameEnabled + - Label: + default: "Miscellaneous" + Parameters: + - TruncatedPipelineStackName + + # This rule is slowing the linting down by 20min+ as it's struggling + # to resolve the conditions in ProgrammaticPermissionsBoundary. + # This can be reconsidered in the future. + cfn-lint: + config: + ignore_checks: + - E3502 + +Parameters: + SAMStackName: + Description: > + The name to use for the SAM stack that will be deployed by this + pipeline, passed to --stack-name to sam deploy + Type: "String" + AllowedPattern: "[a-z0-9][a-z0-9-]{0,21}[a-z0-9]" + ConstraintDescription: > + must consist of lowercase letters, numbers and hyphens; + with no more than 23 characters and must not start or end with hyphen + + TruncatedPipelineStackName: + Description: > + Truncated Pipeline stack name to apply to IAM role names in order to comply + with the 64-character length limits set by AWS. + Type: "String" + Default: "none" + AllowedPattern: "[a-zA-Z0-9][a-zA-Z0-9-]{0,21}[a-zA-Z0-9]" + ConstraintDescription: > + must consist of uppercase, lowercase letters, numbers and hyphens; + with no more than 23 characters and must not start or end with hyphen + + Environment: + Description: "The name of the environment to deploy to" + Type: "String" + AllowedValues: + - build + - staging + - production + - integration + - demo + - local + - dev + + ProgrammaticPermissionsBoundary: + Description: > + Setting this parameter to "True" will create a programmatic + Permissions Boundary, enabling you to choose which services your + application stack needs to run. If set to "False", no services + need to be selected from the parameters below. The PPB has a core + set of services always allowed, for more information please see + documentation at https://govukverify.atlassian.net/wiki/x/NIBOyg. + Default: "False" + Type: "String" + AllowedValues: + - "False" + - "True" + + AllowedServiceOne: + Description: > + Select a service your application needs permissions for (1 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + AllowedServiceTwo: + Description: > + Select a service your application needs permissions for (2 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + AllowedServiceThree: + Description: > + Select a service your application needs permissions for (3 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + AllowedServiceFour: + Description: > + Select a service your application needs permissions for (4 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + AllowedServiceFive: + Description: > + Select a service your application needs permissions for (5 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + AllowedServiceSix: + Description: > + Select a service your application needs permissions for (6 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + AllowedServiceSeven: + Description: > + Select a service your application needs permissions for (7 of 7) + Core services included: CloudFormation, CloudWatch, Logs, CodeBuild, + IAM, KMS, S3, Secrets Manager and Textract. + Default: "none" + Type: "String" + AllowedValues: + - none + - Athena & Glue + - Athena, Glue & Redshift + - Cognito + - DynamoDB + - ECR & ECS + - EC2 + - EventBridge + - Firehose & Kinesis + - Lambda + - PerformanceTest + - QuickSight + - SNS + - SSM + - StepFunctions + - SQS + - Xray + + DynamicResourcesPrefix: + Description: > + (Optional) Prefix for the name of dynamic CloudFormation stacks and any resources + created by them. If not using dynamic resources, leave it as "none". + Type: "String" + Default: "none" + + VpcStackName: + Description: "The name of the VPC to deploy to" + Type: "String" + Default: "none" + + ContainerSignerKmsKeyArn: + Type: "String" + Description: > + (Optional) This is the ARN of the KMS key that signs container images built by GitHub + Actions. This value is returned as the ContainerSignerKmsKeyArn output parameter + from the container-signer Cloudformation stack + Default: "none" + AllowedPattern: "(arn:.*:kms:.*:.*:key/.+)|(none)" + ConstraintDescription: must be the ARN of a KMS key or "none" + + CustomKmsKeyArns: + Type: "CommaDelimitedList" + Default: "" + Description: > + (Optional) ARNs of custom KMS keys that can be used to access secrets. + These must be created separately before deploying the pipeline. + + SigningProfileArn: + Type: "String" + Description: > + (Optional) The ARN of the signing profile to use for signing artifacts. + This value is returned as the SigningProfileArn + output parameter from the signer CloudFormation stack + AllowedPattern: "(arn:.*:signer:.*:.*:/signing-profiles/[^/]+)|(none)" + ConstraintDescription: must be a signing profile ARN (not the profile version ARN) or none + + SigningProfileVersionArn: + Type: "String" + Description: > + (Optional) The versioned profile ARN of the signing profile to use for verifying + that Lambdas deployed by this pipeline were generated from post-merge + build pipeline. This value is returned as the SigningProfileVersionArn + output parameter from the signer CloudFormation stack + AllowedPattern: "(arn:.*:signer:.*:.*:/signing-profiles/.+/.+)|(none)" + ConstraintDescription: must be a signing profile version ARN (not the signing profile ARN) or none + + AdditionalCodeSigningVersionArns: + Type: "String" + Description: > + (Optional) An additional set of CodeSigning profiles that we're allowing to deploy into our lambdas. + This could be AWS signatures for their lambda layers, Dynatrace signatures for their layers, or simply + other artifacts produced that are signed by a third party signing ARN. + Default: "none" + AllowedPattern: "(arn:.*:signer:.*:.*:/signing-profiles/.+/.+)|(none)" + ConstraintDescription: must be a signing profile version ARN (not the signing profile ARN) or none + + InvokableLambdaAccounts: + Type: CommaDelimitedList + Description: > + (Optional) The AWS account ID where lambda's are deployed that you need to invoke from this account or none. + Default: "" + + AccessDynamoDBAccounts: + Type: CommaDelimitedList + Description: > + (Optional) The AWS account ID where DynamoDB Table is. + Default: "" + + ArtifactSourceBucketArn: + Description: > + (Optional) The ARN of the bucket to use as a pipeline source. This would + typically be the ArtifactPromotionBucketArn output from an upstream + pipeline. + Type: "String" + Default: "none" + AllowedPattern: "(arn:.*:s3:::.+)|(none)" + ConstraintDescription: "must be the ARN of an S3 bucket" + + ArtifactSourceBucketEventTriggerRoleArn: + Description: > + (Optional) The ARN of the IAM role assumed by upstream environment CloudWatch events, notifying + any changes to the ArtifactSource bucket. This is one of the components of the event-based trigger. + This would typically be the `ArtifactPromotionBucketEventTriggerRoleArn` output from an upstream pipeline. + Type: "String" + Default: "none" + AllowedPattern: "(arn:.*:iam::.*:role/.+)|(none)" + ConstraintDescription: "must be an IAM Role ARN or none" + + GitHubRepositoryName: + Description: > + (Optional) The name of the GitHub repository (within the govuk-one-login + organization) which initiates this pipeline. This parameter should only + be specified in the account that is called by the GitHub Actions to + initiate the deployment. The GitHub Identity Provider stack must have + been deployed in the same account. + Type: "String" + Default: "none" + AllowedPattern: "[^/]+" + ConstraintDescription: > + must be a GitHub repository name in the govuk-one-login organization (without the + govuk-one-login/ prefix) + + OneLoginRepositoryName: + Description: > + (Optional) The name of the GitHub repository (within the govuk-one-login + organization) which initiates this pipeline. This parameter should only + be specified in the account that is called by the GitHub Actions to + initiate the deployment. The GitHub Identity Provider stack must have + been deployed in the same account. + Type: "String" + Default: "none" + AllowedPattern: "[^/]+" + ConstraintDescription: > + must be a GitHub repository name in the govuk-one-login organization (without the + govuk-one-login/ prefix) + + RequireTestContainerSignatureValidation: + Description: > + (Optional) + Require test container signature to be validated before passing control to the test entrypoint? + Type: "String" + Default: "No" + AllowedValues: + - "Yes" + - "No" + + TestImageRepositoryUri: + Description: | + (Optional) + A URI referring to the ECR repository containing a test image to be + executed within this pipeline. + Type: "String" + Default: "none" + AllowedPattern: "^(?!\\w+://).*" + ConstraintDescription: "must not contain a scheme" + + TestReportFormat: + Description: | + (Optional) + The type of report generated during the test action of the deployment pipeline. + See: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#reports-buildspec-file + Type: "String" + Default: "JUNITXML" + AllowedValues: + - CUCUMBERJSON + - JUNITXML + - NUNITXML + - NUNIT3XML + - TESTNGXML + - VISUALSTUDIOTRX + - none + + TestCoverageReportFormat: + Description: | + (Optional) + The type of coverage report generated during the test action of the deployment pipeline. + See: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#reports-buildspec-file + Type: "String" + Default: "CLOVERXML" + AllowedValues: + - CLOVERXML + - COBERTURAXML + - JACOCOXML + - SIMPLECOV + + TestComputeType: + Description: | + (Optional) + The build environment used for running the tests + Type: "String" + Default: "BUILD_GENERAL1_SMALL" + AllowedValues: + - "BUILD_GENERAL1_SMALL" + - "BUILD_GENERAL1_MEDIUM" + - "BUILD_GENERAL1_LARGE" + - "BUILD_GENERAL1_2XLARGE" + + RunTestContainerInVPC: + Description: | + (Optional) + Requires the testcontainers to run from within the VPC, allowing outbound configuration to be via the EIP, + therefore allow-listable in the WAF. + Any REGIONAL APIs under test will need to be accessible over the VPC NAT Gateway via public internet. + Trying to communicate over a VPC endpoint for Regional API gateways results in Forbbidden 403. + Type: "String" + AllowedValues: + - "True" + - "False" + Default: "False" # Existing behaviour is to run outside, teams need to flip this parameter to 'true' in order to benefit. + + AWSOrganizationId: + Description: "Comma-separated IDs of AWS Organizations where this account and the target pipeline account are members." + Type: "CommaDelimitedList" + Default: "o-pjzf8d99ys,o-dpp53lco28" + AllowedPattern: "^[a-z0-9-]+$" + ConstraintDescription: "must be a valid organization ID, made of lowercase letters and numbers" + + RequireManualApproval: + Description: > + Require a manual approval step before deployment? + Type: "String" + Default: "No" + AllowedValues: + - "Yes" + - "No" + + IncludePromotion: + Description: > + Should a promote stage be included in the pipeline? + Type: "String" + Default: "Yes" + AllowedValues: + - "Yes" + - "No" + + AllowedAccounts: + Description: > + (Optional) List of accounts that can read the promotion bucket (maximum of 2 accounts, separated by a comma) + Type: String + Default: "" + MaxLength: 25 + AllowedPattern: "^([0-9]{12,12}(,[0-9]{12,12})?)?$" + ConstraintDescription: > + (Optional) must consist of a maximum of 2 AWS accounts separated by a comma. Maximum length of 25 characters + + BuildNotificationStackName: + Description: "The name of the BuildNotificationStack, which links to the Slack channel where notifications from this pipeline are sent" + Type: String + Default: "none" + AllowedPattern: "[a-zA-Z0-9-]+|none" + + SlackNotificationType: + Description: > + The events that should be sent to Slack. The options include no + notifications, only build failures, or both successful and failed builds. + Type: String + Default: None + AllowedValues: + - None + - Failures + - All + + LogRetentionDays: + Description: "The number of days that pipeline logs should be retained" + Type: Number + Default: 7 + + ArtifactWriteTimeLoggingLambdaArn: + Description: "The ARN of the Lambda function to log artifact bucket write times to Grafana" + Type: String + Default: None + + AbortOnCheckovFailure: + Description: "Whether to ABORT or CONTINUE the CodeBuild deployment if the pre_build checkov check fails" + Type: String + Default: CONTINUE + AllowedValues: + - CONTINUE + - ABORT + + AccessLogsCustomBucketNameEnabled: + Description: > + Controls the S3AccessLogsBucket BucketName. + By default (Yes) the S3AccessLogsBucket is named as {AWS::StackName}-{Pseudo random element from stack id}. This allows the bucket name to be found programatically and avoids naming collisions within the account. + When set to No the bucket will be named by Cloudformation standard resource naming approach. Please seek guidance from #dev-platform before setting 'No' + Type: "String" + Default: "Yes" + AllowedValues: + - "Yes" + - "No" + + PipelineEnvironmentNameEnabled: + Description: > + When set to default "No" the bucket will be named by Cloudformation standard resource naming approach. + By setting this to "Yes" the pipeline will be named as {AWS::StackName}-{Environment}-Pipeline-{Pseudo random element from stack id}. + This allows to easier identify the environment origin when notifications are sent that includes the pipeline name. + Please seek guidance from #dev-platform before setting 'Yes' + Type: "String" + Default: "No" + AllowedValues: + - "Yes" + - "No" + + ECSCanaryDeployment: + Description: > + Canary strategy to be used with your ECS application. + Must be "None" if not using ecs-canary-deployment stack. + Type: String + Default: None + AllowedValues: + - None + - CodeDeployDefault.ECSCanary10Percent5Minutes + - CodeDeployDefault.ECSCanary10percent15Minutes + - CodeDeployDefault.ECSAllAtOnce + - ECSCanary50Percent5Minutes + + LambdaCanaryDeployment: + Description: > + Canary strategy to be used with your Lambda applications + Type: String + Default: None + AllowedValues: + - None + - AllAtOnce + - Canary10Percent5Minutes + - Canary10Percent10Minutes + - Canary10Percent15Minutes + - Canary10Percent30Minutes + + TrafficTestImageRepositoryUri: + Description: | + (Optional) + A URI referring to the ECR repository containing a image to be + executed along side the deployment for canary deployment testing. + Type: "String" + Default: "none" + AllowedPattern: "^(?!\\w+://).*" + ConstraintDescription: "must not contain a scheme" + +Rules: + GitHubRepositoryOmittedWhenTriggeredByUpstreamPipeline: + RuleCondition: !Not + - Fn::Equals: [ !Ref ArtifactSourceBucketArn, "none" ] + Assertions: + - Assert: + Fn::Equals: [ !Ref GitHubRepositoryName, "none" ] + AssertDescription: > + A GitHub repository cannot also be specified if this pipeline is + triggered by an upstream pipeline. It must be "none". + + OneloginRepositoryOmittedWhenTriggeredByUpstreamPipeline: + RuleCondition: !Not + - Fn::Equals: [ !Ref ArtifactSourceBucketArn, "none" ] + Assertions: + - Assert: + !Equals [ !Ref OneLoginRepositoryName, "none" ] + AssertDescription: > + A GitHub repository cannot also be specified if this pipeline is + triggered by an upstream pipeline. It must be "none". + + UpstreamPipelineOmittedWhenTriggeredByGitHubRepository: + RuleCondition: !Not + - Fn::And: + - Fn::Equals: [ !Ref GitHubRepositoryName, "none" ] + - Fn::Equals: [ !Ref OneLoginRepositoryName, "none" ] + Assertions: + - Assert: + Fn::Equals: [ !Ref ArtifactSourceBucketArn, "none" ] + AssertDescription: > + An upstream pipeline cannot also be specified if this pipeline is + triggered by a GitHub repository. It must be "none". + + SigningKeyParameter: + RuleCondition: !Not + - Fn::Equals: [ !Ref SigningProfileArn, "none" ] + Assertions: + - Assert: !Not + - Fn::Equals: [ !Ref SigningProfileVersionArn, "none" ] + AssertDescription: > + When SigningProfileArn is supplied then + SigningProfileVersionArn must also be supplied. + + SigningKeyVersionParameter: + RuleCondition: !Not + - Fn::Equals: [ !Ref SigningProfileVersionArn, "none" ] + Assertions: + - Assert: !Not + - Fn::Equals: [ !Ref SigningProfileArn, "none" ] + AssertDescription: > + When SigningProfileVersionArn is supplied then + SigningProfileArn must also be supplied. + + PipelineSourceIsRequired: + Assertions: + - Assert: !Not + - Fn::And: + - !Equals [ !Ref GitHubRepositoryName, "none" ] + - !Equals [ !Ref OneLoginRepositoryName, "none" ] + - !Equals [ !Ref ArtifactSourceBucketArn, "none" ] + AssertDescription: The pipeline must have a source. + + BuildNotificationStackNameIsRequiredWhenSlackNotificationTypeIsSelected: + RuleCondition: !Not + - Fn::Equals: [ !Ref SlackNotificationType, "None" ] + Assertions: + - Assert: + Fn::Not: [ !Equals [ !Ref BuildNotificationStackName, "none" ]] + AssertDescription: > + BuildNotificationStackName must be provided if a SlackNotificationType is selected. + + AllowedAccountsAreRequiredWhenPromotionIsEnabled: + RuleCondition: !Equals [ !Ref IncludePromotion, "Yes" ] + Assertions: + - Assert: !Not + - Fn::Or: + - !Equals [ !Ref AllowedAccounts, "" ] + - !Equals [ !Ref AllowedAccounts, "," ] + AssertDescription: > + AllowedAccounts must be provided if IncludePromotion is enabled. + + DevEnvironmentsCantPromote: + RuleCondition: !Equals [ !Ref IncludePromotion, "Yes" ] + Assertions: + - Assert: !Not + - Fn::Or: + - !Equals [ !Ref Environment, "dev" ] + - !Equals [ !Ref Environment, "local" ] + - !Equals [ !Ref Environment, "demo" ] + AssertDescription: > + Only build or staging supports promotion. + + ProgrammaticPermissionsBoundaryParameter: + RuleCondition: !Equals [ !Ref ProgrammaticPermissionsBoundary, "False" ] + Assertions: + - Assert: !And + - !Equals [ !Ref AllowedServiceOne, "none" ] + - !Equals [ !Ref AllowedServiceTwo, "none" ] + - !Equals [ !Ref AllowedServiceThree, "none" ] + - !Equals [ !Ref AllowedServiceFour, "none" ] + - !Equals [ !Ref AllowedServiceFive, "none" ] + - !Equals [ !Ref AllowedServiceSix, "none" ] + - !Equals [ !Ref AllowedServiceSeven, "none" ] + AssertDescription: > + When selecting any of the PermissionBoundary parameters the + ProgrammaticPermissionsBoundary parameter needs to be set to "True" + + +Outputs: + PipelineName: + Description: "The name of the pipeline provisioned" + Value: !Ref Pipeline + + ArtifactPromotionBucketArn: + Condition: CreatePromoteStage + Description: > + The ARN of the bucket containing artifacts to be promoted to higher + environments. + Value: !GetAtt ArtifactPromotionBucket.Arn + + ArtifactPromotionBucketEventTriggerRoleArn: + Condition: CreatePromoteStage + Description: > + The ARN of the IAM role CloudWatch Events assume to notify the higher environments + any changes to the promotion bucket. This is one of the components of the event-based trigger. + Value: !GetAtt PromotionTriggerCloudWatchEventRole.Arn + + GitHubActionsRoleArn: + Condition: CreateGitHubActionsResources + Description: > + Role ARN for deploying and signing artifacts in GitHub Actions. + Restricted for usage only in the repository specified in the + `GitHubRepositoryName` and/or `OneLoginRepositoryName` parameter + and on branches named `main`. + Value: !GetAtt GitHubActionsRole.Arn + + GitHubActionsRoleName: + Condition: CreateGitHubActionsResources + Description: > + Role name for deploying and signing artifacts in GitHub Actions. + Restricted for usage only in the repository specified in the + `GitHubRepositoryName` and/or `OneLoginRepositoryName` parameter + and on branches named `main`. + Value: !Ref GitHubActionsRole + Export: + Name: !Sub "${AWS::StackName}-GitHubActionsRoleName" + + GitHubActionsValidateRoleArn: + Condition: CreateGitHubActionsResources + Description: > + Role ARN for running `sam validate` in GitHub Actions. Restricted for + usage only in the repository specified in the `GitHubRepositoryName` + and/or `OneLoginRepositoryName` parameter. + Value: !GetAtt GitHubActionsValidateRole.Arn + + GitHubArtifactSourceBucketName: + Condition: CreateGitHubActionsResources + Description: > + The name of the bucket into which GitHub should publish artifacts for + deployment. + Value: !Ref GitHubArtifactSourceBucket + +Conditions: + SigningProfile: + Fn::And: + - Fn::Not: + - Fn::Equals: + - !Ref SigningProfileArn + - "none" + - Fn::Not: + - Fn::Equals: + - !Ref SigningProfileVersionArn + - "none" + + AccessLogsCustomBucketName: + Fn::Equals: + - !Ref AccessLogsCustomBucketNameEnabled + - "Yes" + + PipelineEnvironmentNameEnabled: + Fn::Equals: + - !Ref PipelineEnvironmentNameEnabled + - "Yes" + + AdditionalCodeSigningProfiles: + Fn::Not: + - Fn::Equals: + - !Ref AdditionalCodeSigningVersionArns + - "none" + + CreateContainerSignerResources: + Fn::Or: + - Fn::Equals: + - !Ref Environment + - "build" + - Fn::Equals: + - !Ref Environment + - "demo" + - Fn::Equals: + - !Ref Environment + - "dev" + - Fn::Equals: + - !Ref Environment + - "local" + + NotProduction: + Fn::Not: + - Fn::Equals: + - !Ref Environment + - "production" + + IsDevDemoLocal: + Fn::Or: + - Fn::Equals: + - !Ref Environment + - "dev" + - Fn::Equals: + - !Ref Environment + - "demo" + - Fn::Equals: + - !Ref Environment + - "local" + + IsContainerSignerProvidedInParameters: + Fn::Not: + - Fn::Equals: + - !Ref ContainerSignerKmsKeyArn + - "none" + + CreateContainerSignerPolicy: + Fn::And: + - !Condition CreateContainerSignerResources + - !Condition IsContainerSignerProvidedInParameters + + CreateCustomKMSKeyPolicy: + Fn::Not: + - Fn::Equals: + - !Join ['', !Ref CustomKmsKeyArns] + - "" + + CreateAlphaGovRepoResources: + Fn::Not: + - Fn::Equals: + - !Ref GitHubRepositoryName + - "none" + + CreateOneLoginRepoResources: + Fn::Not: + - Fn::Equals: + - !Ref OneLoginRepositoryName + - "none" + + CreateGitHubActionsResources: + Fn::Or: [Condition: CreateAlphaGovRepoResources, Condition: CreateOneLoginRepoResources] + + CreateAlphaGovActionDevLocal: + Fn::And: + - Condition: CreateAlphaGovRepoResources + - Condition: IsDevDemoLocal + + CreateAlphaGovActionBuild: + Fn::And: + - Condition: CreateAlphaGovRepoResources + - Fn::Not: + - Condition: IsDevDemoLocal + + CreateOneLoginDevLocal: + Fn::And: + - Condition: CreateOneLoginRepoResources + - Condition: IsDevDemoLocal + + CreateOneLoginBuild: + Fn::And: + - Condition: CreateOneLoginRepoResources + - Fn::Not: + - Condition: IsDevDemoLocal + + CreateDeploymentTriggerEventBusPolicy: + Fn::Not: + - Fn::Equals: + - !Ref ArtifactSourceBucketEventTriggerRoleArn + - "none" + + CreateManualApprovalStage: + Fn::Equals: + - !Ref RequireManualApproval + - "Yes" + + CreateTrafficTest: + Fn::Not: + - Fn::Equals: + - !Ref TrafficTestImageRepositoryUri + - "none" + + CreateTestAction: + Fn::Not: + - Fn::Equals: + - !Ref TestImageRepositoryUri + - "none" + + CreateTestContainerSignatureValidateStage: + Fn::And: + - !Condition CreateTestAction + - Fn::Equals: + - !Ref RequireTestContainerSignatureValidation + - "Yes" + - !Condition IsContainerSignerProvidedInParameters + + CreateVPCConfigurationForTestContainer: + Fn::And: + - !Condition CreateTestAction + - !Condition NotProduction + - Fn::Equals: + - !Ref RunTestContainerInVPC + - "True" + + CreatePromoteStage: + Fn::Equals: + - !Ref IncludePromotion + - "Yes" + + EnablePipelinePolling: + Fn::And: + - Fn::Not: + - Fn::Equals: + - !Ref ArtifactSourceBucketArn + - "none" + - Fn::Equals: + - !Ref ArtifactSourceBucketEventTriggerRoleArn + - "none" + + EmitBuildNotifications: + Fn::Not: + - Fn::Equals: + - !Ref SlackNotificationType + - "None" + + InvokableLambda: + Fn::Not: + - Fn::Equals: + - !Join ['', !Ref InvokableLambdaAccounts] + - "" + + AccessDynamoTable: + Fn::Not: + - Fn::Equals: + - !Join ['', !Ref AccessDynamoDBAccounts] + - "" + + EmitGitHubArtifactWriteLogs: + Fn::And: + - Fn::Not: + - Fn::Equals: + - !Ref ArtifactWriteTimeLoggingLambdaArn + - "None" + - !Condition CreateGitHubActionsResources + + EmitPromotionArtifactWriteLogs: + Fn::And: + - Fn::Not: + - Fn::Equals: + - !Ref ArtifactWriteTimeLoggingLambdaArn + - "None" + - !Condition CreatePromoteStage + + UseTwoAllowedAccounts: + Fn::Not: + - Fn::Equals: + - Fn::Select: + - 1 + - Fn::Split: + - "," + - Fn::Sub: + - "${AccountIds},,," + - AccountIds: !Ref AllowedAccounts + - "" + + UseTruncatedPipelineStackName: + Fn::Not: + - Fn::Equals: + - !Ref TruncatedPipelineStackName + - "none" + + UseProgrammaticPermissionsBoundary: !Equals + - !Ref ProgrammaticPermissionsBoundary + - "True" + + IncludeAthenaAndGlue: !Or + - !Or + - !Equals + - !Ref AllowedServiceOne + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceOne + - "Athena, Glue & Redshift" + - !Or + - !Equals + - !Ref AllowedServiceTwo + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceTwo + - "Athena, Glue & Redshift" + - !Or + - !Equals + - !Ref AllowedServiceThree + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceThree + - "Athena, Glue & Redshift" + - !Or + - !Equals + - !Ref AllowedServiceFour + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceFour + - "Athena, Glue & Redshift" + - !Or + - !Equals + - !Ref AllowedServiceFive + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceFive + - "Athena, Glue & Redshift" + - !Or + - !Equals + - !Ref AllowedServiceSix + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceSix + - "Athena, Glue & Redshift" + - !Or + - !Equals + - !Ref AllowedServiceSeven + - "Athena & Glue" + - !Equals + - !Ref AllowedServiceSeven + - "Athena, Glue & Redshift" + + IncludeAthenaGlueRedshift: !Or + - !Equals + - !Ref AllowedServiceOne + - "Athena, Glue & Redshift" + - !Equals + - !Ref AllowedServiceTwo + - "Athena, Glue & Redshift" + - !Equals + - !Ref AllowedServiceThree + - "Athena, Glue & Redshift" + - !Equals + - !Ref AllowedServiceFour + - "Athena, Glue & Redshift" + - !Equals + - !Ref AllowedServiceFive + - "Athena, Glue & Redshift" + - !Equals + - !Ref AllowedServiceSix + - "Athena, Glue & Redshift" + - !Equals + - !Ref AllowedServiceSeven + - "Athena, Glue & Redshift" + + IncludeCognito: !Or + - !Equals + - !Ref AllowedServiceOne + - "Cognito" + - !Equals + - !Ref AllowedServiceTwo + - "Cognito" + - !Equals + - !Ref AllowedServiceThree + - "Cognito" + - !Equals + - !Ref AllowedServiceFour + - "Cognito" + - !Equals + - !Ref AllowedServiceFive + - "Cognito" + - !Equals + - !Ref AllowedServiceSix + - "Cognito" + - !Equals + - !Ref AllowedServiceSeven + - "Cognito" + + IncludeDynamoDB: !Or + - !Equals + - !Ref AllowedServiceOne + - "DynamoDB" + - !Equals + - !Ref AllowedServiceTwo + - "DynamoDB" + - !Equals + - !Ref AllowedServiceThree + - "DynamoDB" + - !Equals + - !Ref AllowedServiceFour + - "DynamoDB" + - !Equals + - !Ref AllowedServiceFive + - "DynamoDB" + - !Equals + - !Ref AllowedServiceSix + - "DynamoDB" + - !Equals + - !Ref AllowedServiceSeven + - "DynamoDB" + + IncludeECRAndECS: !Or + - !Equals + - !Ref AllowedServiceOne + - "ECR & ECS" + - !Equals + - !Ref AllowedServiceTwo + - "ECR & ECS" + - !Equals + - !Ref AllowedServiceThree + - "ECR & ECS" + - !Equals + - !Ref AllowedServiceFour + - "ECR & ECS" + - !Equals + - !Ref AllowedServiceFive + - "ECR & ECS" + - !Equals + - !Ref AllowedServiceSix + - "ECR & ECS" + - !Equals + - !Ref AllowedServiceSeven + - "ECR & ECS" + + IncludeEC2: !Or + - !Equals + - !Ref AllowedServiceOne + - "EC2" + - !Equals + - !Ref AllowedServiceTwo + - "EC2" + - !Equals + - !Ref AllowedServiceThree + - "EC2" + - !Equals + - !Ref AllowedServiceFour + - "EC2" + - !Equals + - !Ref AllowedServiceFive + - "EC2" + - !Equals + - !Ref AllowedServiceSix + - "EC2" + - !Equals + - !Ref AllowedServiceSeven + - "EC2" + + IncludeEventBridge: !Or + - !Equals + - !Ref AllowedServiceOne + - "EventBridge" + - !Equals + - !Ref AllowedServiceTwo + - "EventBridge" + - !Equals + - !Ref AllowedServiceThree + - "EventBridge" + - !Equals + - !Ref AllowedServiceFour + - "EventBridge" + - !Equals + - !Ref AllowedServiceFive + - "EventBridge" + - !Equals + - !Ref AllowedServiceSix + - "EventBridge" + - !Equals + - !Ref AllowedServiceSeven + - "EventBridge" + + IncludeFirehoseAndKinesis: !Or + - !Equals + - !Ref AllowedServiceOne + - "Firehose & Kinesis" + - !Equals + - !Ref AllowedServiceTwo + - "Firehose & Kinesis" + - !Equals + - !Ref AllowedServiceThree + - "Firehose & Kinesis" + - !Equals + - !Ref AllowedServiceFour + - "Firehose & Kinesis" + - !Equals + - !Ref AllowedServiceFive + - "Firehose & Kinesis" + - !Equals + - !Ref AllowedServiceSix + - "Firehose & Kinesis" + - !Equals + - !Ref AllowedServiceSeven + - "Firehose & Kinesis" + + IncludeLambda: !Or + - !Equals + - !Ref AllowedServiceOne + - "Lambda" + - !Equals + - !Ref AllowedServiceTwo + - "Lambda" + - !Equals + - !Ref AllowedServiceThree + - "Lambda" + - !Equals + - !Ref AllowedServiceFour + - "Lambda" + - !Equals + - !Ref AllowedServiceFive + - "Lambda" + - !Equals + - !Ref AllowedServiceSix + - "Lambda" + - !Equals + - !Ref AllowedServiceSeven + - "Lambda" + IncludePerformanceTest: !Or + - !Equals + - !Ref AllowedServiceOne + - "PerformanceTest" + - !Equals + - !Ref AllowedServiceTwo + - "PerformanceTest" + - !Equals + - !Ref AllowedServiceThree + - "PerformanceTest" + - !Equals + - !Ref AllowedServiceFour + - "PerformanceTest" + - !Equals + - !Ref AllowedServiceFive + - "PerformanceTest" + - !Equals + - !Ref AllowedServiceSix + - "PerformanceTest" + - !Equals + - !Ref AllowedServiceSeven + - "PerformanceTest" + + IncludeQuickSight: !Or + - !Equals + - !Ref AllowedServiceOne + - "QuickSight" + - !Equals + - !Ref AllowedServiceTwo + - "QuickSight" + - !Equals + - !Ref AllowedServiceThree + - "QuickSight" + - !Equals + - !Ref AllowedServiceFour + - "QuickSight" + - !Equals + - !Ref AllowedServiceFive + - "QuickSight" + - !Equals + - !Ref AllowedServiceSix + - "QuickSight" + - !Equals + - !Ref AllowedServiceSeven + - "QuickSight" + + IncludeSNS: !Or + - !Equals + - !Ref AllowedServiceOne + - "SNS" + - !Equals + - !Ref AllowedServiceTwo + - "SNS" + - !Equals + - !Ref AllowedServiceThree + - "SNS" + - !Equals + - !Ref AllowedServiceFour + - "SNS" + - !Equals + - !Ref AllowedServiceFive + - "SNS" + - !Equals + - !Ref AllowedServiceSix + - "SNS" + - !Equals + - !Ref AllowedServiceSeven + - "SNS" + + IncludeSSM: !Or + - !Equals + - !Ref AllowedServiceOne + - "SSM" + - !Equals + - !Ref AllowedServiceTwo + - "SSM" + - !Equals + - !Ref AllowedServiceThree + - "SSM" + - !Equals + - !Ref AllowedServiceFour + - "SSM" + - !Equals + - !Ref AllowedServiceFive + - "SSM" + - !Equals + - !Ref AllowedServiceSix + - "SSM" + - !Equals + - !Ref AllowedServiceSeven + - "SSM" + + IncludeStepFunctions: !Or + - !Equals + - !Ref AllowedServiceOne + - "StepFunctions" + - !Equals + - !Ref AllowedServiceTwo + - "StepFunctions" + - !Equals + - !Ref AllowedServiceThree + - "StepFunctions" + - !Equals + - !Ref AllowedServiceFour + - "StepFunctions" + - !Equals + - !Ref AllowedServiceFive + - "StepFunctions" + - !Equals + - !Ref AllowedServiceSix + - "StepFunctions" + - !Equals + - !Ref AllowedServiceSeven + - "StepFunctions" + + IncludeSQS: !Or + - !Equals + - !Ref AllowedServiceOne + - "SQS" + - !Equals + - !Ref AllowedServiceTwo + - "SQS" + - !Equals + - !Ref AllowedServiceThree + - "SQS" + - !Equals + - !Ref AllowedServiceFour + - "SQS" + - !Equals + - !Ref AllowedServiceFive + - "SQS" + - !Equals + - !Ref AllowedServiceSix + - "SQS" + - !Equals + - !Ref AllowedServiceSeven + - "SQS" + + IncludeXray: !Or + - !Equals + - !Ref AllowedServiceOne + - "Xray" + - !Equals + - !Ref AllowedServiceTwo + - "Xray" + - !Equals + - !Ref AllowedServiceThree + - "Xray" + - !Equals + - !Ref AllowedServiceFour + - "Xray" + - !Equals + - !Ref AllowedServiceFive + - "Xray" + - !Equals + - !Ref AllowedServiceSix + - "Xray" + - !Equals + - !Ref AllowedServiceSeven + - "Xray" + + IncludeDynamicResources: + Fn::Not: + - Fn::Equals: + - !Ref DynamicResourcesPrefix + - "none" + + IncludeDynamicSQS: + Fn::And: + - Condition: IncludeDynamicResources + - Condition: IncludeSQS + + DeployECSCanaryStack: + Fn::Not: + - Fn::Equals: + - !Ref ECSCanaryDeployment + - None + +Mappings: + BuildNotifications: + "All": + EventTypeIds: + - codepipeline-pipeline-pipeline-execution-failed + - codepipeline-pipeline-pipeline-execution-succeeded + "Failures": + EventTypeIds: + - codepipeline-pipeline-pipeline-execution-failed + "None": + EventTypeIds: [] + # See https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-access-logs.html + # ElasticLoadBalancerAccountIds: + # eu-west-2: + # AccountId: 652711504416 + +Resources: + LogsKmsKey: + Type: AWS::KMS::Key + Properties: + EnableKeyRotation: true + KeyPolicy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" + Action: + - "kms:*" + Resource: + - "*" + - Effect: "Allow" + Principal: + Service: !Sub "logs.${AWS::Region}.amazonaws.com" + Action: + - "kms:Encrypt*" + - "kms:Decrypt*" + - "kms:ReEncrypt*" + - "kms:GenerateDataKey*" + - "kms:Describe*" + Resource: + - "*" + Condition: + ArnLike: + "kms:EncryptionContext:aws:logs:arn": !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:*" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "LogsKmsKey" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # S3 Access Logs Bucket + # + + S3AccessLogsBucket: + Type: AWS::S3::Bucket + # checkov:skip=CKV_AWS_18:This is the access logs bucket. It should not log itself. + Properties: + LifecycleConfiguration: + Rules: + - Id: artifactBucketExpiry + Status: Enabled + ExpirationInDays: 30 + BucketName: !If + - AccessLogsCustomBucketName + - !Join + - "-" + - - !Ref AWS::StackName + - "s3logs" + - Fn::Select: + - 4 + - Fn::Split: + - '-' + - Fn::Select: + - 2 + - Fn::Split: + - / + - Ref: AWS::StackId + - !Ref AWS::NoValue + VersioningConfiguration: + Status: "Enabled" + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "s3logs" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + - Key: CheckovRulesToSkip + Value: CKV_AWS_18 + + + S3AccessLogsBucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref S3AccessLogsBucket + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "EnableS3Logging" + Effect: "Allow" + Resource: + - !Sub "${S3AccessLogsBucket.Arn}/*" + Principal: + Service: "logging.s3.amazonaws.com" + Action: + - "s3:PutObject" + Condition: + StringEquals: + "aws:SourceAccount": !Sub "${AWS::AccountId}" + Bool: + "aws:SecureTransport": true + + # + # GitHub Artifact Source + # + + GitHubArtifactSourceBucket: + Condition: CreateGitHubActionsResources + Type: AWS::S3::Bucket + Properties: + LifecycleConfiguration: + Rules: + - Id: artifactBucketExpiry + Status: Enabled + ExpirationInDays: 180 + VersioningConfiguration: + Status: "Enabled" + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + LoggingConfiguration: + DestinationBucketName: !Ref S3AccessLogsBucket + LogFilePrefix: "pipeline/github/" + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: "AES256" + NotificationConfiguration: + !If + - EmitGitHubArtifactWriteLogs + - LambdaConfigurations: + - Event: s3:ObjectCreated:Put + Function: !Ref ArtifactWriteTimeLoggingLambdaArn + - !Ref AWS::NoValue + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "githubartifactsourcebucket" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + - Key: "ApplicationName" + Value: !Ref SAMStackName + - Key: "Environment" + Value: !Ref Environment + + GitHubArtifactSourceBucketPolicy: + Condition: CreateGitHubActionsResources + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref GitHubArtifactSourceBucket + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Resource: + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + Principal: + AWS: !GetAtt GitHubActionsRole.Arn + Action: + - "s3:PutObject" + - "s3:PutObjectAcl" + - "s3:ListBucket" + Condition: + Bool: + "aws:SecureTransport": true + + # + # Artifact Promotion + # + + ArtifactPromotionBucket: + Condition: CreatePromoteStage + Type: AWS::S3::Bucket + Properties: + LifecycleConfiguration: + Rules: + - Id: artifactBucketExpiry + Status: Enabled + ExpirationInDays: 180 + VersioningConfiguration: + Status: "Enabled" + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + LoggingConfiguration: + DestinationBucketName: !Ref S3AccessLogsBucket + LogFilePrefix: "pipeline/promotion/" + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: "AES256" + NotificationConfiguration: + !If + - EmitPromotionArtifactWriteLogs + - LambdaConfigurations: + - Event: s3:ObjectCreated:Put + Function: !Ref ArtifactWriteTimeLoggingLambdaArn + - !Ref AWS::NoValue + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "artifactpromotionbucket" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + - Key: "ApplicationName" + Value: !Ref SAMStackName + - Key: "Environment" + Value: !Ref Environment + + ArtifactPromotionBucketPolicy: + Condition: CreatePromoteStage + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref ArtifactPromotionBucket + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Resource: + - !GetAtt ArtifactPromotionBucket.Arn + - !Sub "${ArtifactPromotionBucket.Arn}/*" + Principal: + AWS: !Split [",", !Ref AllowedAccounts] + Action: + - "s3:GetBucketVersioning" + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetObjectTagging" + - "s3:ListBucket" + Condition: + StringEquals: + "aws:PrincipalOrgID": !Ref AWSOrganizationId + Bool: + "aws:SecureTransport": true + + # + # Deny resources creation outside UK region + # + + LockToRegionPolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "LockToRegionPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Resource: + - "*" + NotAction: + - "a4b:*" + - "acm:*" + - "aws-marketplace-management:*" + - "aws-marketplace:*" + - "budgets:*" + - "ce:*" + - "chime:*" + - "cloudfront:*" + - "cognito-idp:*" + - "config:*" + - "cur:*" + - "directconnect:*" + - "ec2:Describe*" + - "fms:*" + - "globalaccelerator:*" + - "health:*" + - "iam:*" + - "importexport:*" + - "kms:*" + - "mobileanalytics:*" + - "networkmanager:*" + - "organizations:*" + - "pricing:*" + - "pipes:*" + - "route53:*" + - "route53domains:*" + - "s3:GetAccountPublic*" + - "s3:ListAllMyBuckets" + - "s3:PutAccountPublic*" + - "ses:*" + - "shield:*" + - "sts:*" + - "support:*" + - "synthetics:*" + - "trustedadvisor:*" + - "waf-regional:*" + - "waf:*" + - "wafv2:*" + - "wellarchitected:*" + Condition: + StringNotEquals: + "aws:RequestedRegion": [ + "eu-west-2" + ] + + # + # GitHub Actions Integration + # + + ContainerSignerKmsKey: + Type: AWS::KMS::Key + Condition: CreateContainerSignerResources + #checkov:skip=CKV_AWS_7:Automatic key rotation can only be enabled on symmetric keys + DeletionPolicy: Retain + UpdateReplacePolicy: Retain + Properties: + Description: Asymmetric key used to sign and validate ECR container artifacts + Enabled: true + KeySpec: RSA_4096 + KeyUsage: SIGN_VERIFY + KeyPolicy: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + AWS: !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" + Action: + - "kms:*" + Resource: + - "*" + - !If + - CreatePromoteStage + - Effect: "Allow" + Principal: + AWS: !Split [",", !Ref AllowedAccounts] + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Verify" + Resource: + - "*" + - !Ref AWS::NoValue + - Effect: "Allow" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Sign" + Resource: + - "*" + Condition: + StringEquals: + "kms:ViaService": !Sub "ecr.${AWS::Region}.amazonaws.com" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "ContainerSignerKmsKey" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + - Key: CheckovRulesToSkip + Value: CKV_AWS_7 + + GitHubActionsPolicy: + Type: AWS::IAM::ManagedPolicy + Condition: CreateGitHubActionsResources + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "GitHubActionsPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "AccessToGitHubArtifactSourceBucket" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:PutObject" + - "s3:GetObjectTagging" + - "s3:GetObjectVersion" + - "s3:ListBucket" + Resource: + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + - Sid: "SAMValidate" + Effect: "Allow" + Action: + - "iam:ListPolicies" + Resource: "*" + - Sid: "AccessToECR" + Effect: "Allow" + Action: + - "ecr:GetAuthorizationToken" + Resource: "*" + - !If + - SigningProfile + - Sid: "ArtifactSigningWithProfile" + Effect: "Allow" + Action: + - "signer:GetSigningProfile" + - "signer:StartSigningJob" + Resource: !Ref SigningProfileArn + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "ArtifactSigningStatusQueries" + Effect: "Allow" + Action: + - "signer:ListSigningJobs" + - "signer:ListSigningProfiles" + - "signer:DescribeSigningJob" + Resource: "*" + - !Ref AWS::NoValue + - !If + - CreateContainerSignerResources + - Sid: "DefaultContainerSigning" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Sign" + Resource: + - !GetAtt ContainerSignerKmsKey.Arn + - !Ref AWS::NoValue + - !If + - CreateContainerSignerPolicy + - Sid: "ContainerSigning" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Sign" + Resource: + - !Ref ContainerSignerKmsKeyArn + - !Ref AWS::NoValue + + + GitHubActionsRole: + Type: AWS::IAM::Role + Condition: CreateGitHubActionsResources + # checkov:skip=GDS_AWS_1:Don't run GDS_AWS_1 + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Fn::If: + - CreateAlphaGovActionDevLocal + - Effect: "Allow" + Action: "sts:AssumeRoleWithWebIdentity" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Condition: + StringLike: + "token.actions.githubusercontent.com:sub": + - !Sub "repo:alphagov/${GitHubRepositoryName}*ref:refs/heads/*" + - !Sub "repo:alphagov/${GitHubRepositoryName}:environment:*" + - !Ref AWS::NoValue + - Fn::If: + - CreateAlphaGovActionBuild + - Effect: "Allow" + Action: "sts:AssumeRoleWithWebIdentity" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Condition: + StringLike: + "token.actions.githubusercontent.com:sub": + - !Sub "repo:alphagov/${GitHubRepositoryName}*ref:refs/heads/main" + - !Ref AWS::NoValue + - Fn::If: + - CreateOneLoginDevLocal + - Effect: "Allow" + Action: "sts:AssumeRoleWithWebIdentity" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Condition: + StringLike: + "token.actions.githubusercontent.com:sub": + - !Sub "repo:govuk-one-login/${OneLoginRepositoryName}*ref:refs/heads/*" + - !Sub "repo:govuk-one-login/${OneLoginRepositoryName}:environment:*" + - !Ref AWS::NoValue + - Fn::If: + - CreateOneLoginBuild + - Effect: "Allow" + Action: "sts:AssumeRoleWithWebIdentity" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Condition: + StringLike: + "token.actions.githubusercontent.com:sub": + - !Sub "repo:govuk-one-login/${OneLoginRepositoryName}*ref:refs/heads/main" + - !Ref AWS::NoValue + ManagedPolicyArns: + - !Ref GitHubActionsPolicy + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "GitHubActionsRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + GitHubActionsValidatePolicy: + Condition: CreateGitHubActionsResources + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "GitHubActionsValidatePolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "SAMValidate" + Effect: "Allow" + Action: + - "iam:ListPolicies" + Resource: "*" + + GitHubActionsValidateRole: + Type: AWS::IAM::Role + Condition: CreateGitHubActionsResources + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - !If + - CreateAlphaGovRepoResources + - Effect: "Allow" + Action: "sts:AssumeRoleWithWebIdentity" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Condition: + StringLike: + "token.actions.githubusercontent.com:sub": !Sub "repo:alphagov/${GitHubRepositoryName}:*" + - !Ref AWS::NoValue + - !If + - CreateOneLoginRepoResources + - Effect: "Allow" + Action: "sts:AssumeRoleWithWebIdentity" + Principal: + Federated: !ImportValue GitHubIdentityProviderArn + Condition: + StringLike: + "token.actions.githubusercontent.com:sub": !Sub "repo:govuk-one-login/${OneLoginRepositoryName}:*" + - !Ref AWS::NoValue + ManagedPolicyArns: + - !Ref GitHubActionsValidatePolicy + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "GitHubActionsValidateRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Shared Code Build Policy + # + + CodeBuildPolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "CodeBuildPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:PutLogEvents" + Effect: "Allow" + - Resource: + - !GetAtt PipelineBucket.Arn + - !Sub "${PipelineBucket.Arn}/*" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetBucketVersioning" + - "s3:PutObjectAcl" + - "s3:PutObject" + Effect: "Allow" + - Resource: + - !Sub "arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/*" + Action: + - "codebuild:CreateReportGroup" + - "codebuild:CreateReport" + - "codebuild:UpdateReport" + - "codebuild:BatchPutTestCases" + Effect: "Allow" + - Resource: + - !Sub "arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:project/*-${SAMStackName}" + Action: + - "codebuild:CreateProject" + - "codebuild:DeleteProject" + - "codebuild:UpdateProject" + Effect: "Allow" + + # + # Deploy (CodeBuild) + # + + CodeSigningConfig: + Condition: SigningProfile + Type: AWS::Lambda::CodeSigningConfig + Properties: + Description: !Sub "Code Signing Config for ${AWS::StackName}" + AllowedPublishers: + SigningProfileVersionArns: !If + - AdditionalCodeSigningProfiles + - !Split [",", !Sub "${SigningProfileVersionArn},${AdditionalCodeSigningVersionArns}"] + - !Split [",", !Ref SigningProfileVersionArn] + CodeSigningPolicies: + UntrustedArtifactOnDeployment: "Enforce" + + AppPermissionsBoundary: + Type: AWS::IAM::ManagedPolicy + Properties: + # checkov:skip=CKV_AWS_111:states:Send* are write actions but do not take a resource + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "AppPermissionsBoundary" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "logs:CreateLog*" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - Effect: "Allow" + Action: + - "logs:PutLogEvents" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:log-stream:*" + - Effect: "Allow" + Action: + - "logs:*LogDelivery" + - "logs:ListL*" + - "logs:*LogEvents" + - "logs:PutRes*" + - "logs:DescribeR*" + - "logs:DescribeLog*" + - "sns:ListTopics" + Resource: + - "*" + - Effect: "Allow" + Action: + - "cloudwatch:PutMetricData" + Resource: + - "*" + - Effect: "Allow" + Action: + - "sns:Publish" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:*" + Condition: + StringEquals: + "aws:PrincipalOrgID": !Ref AWSOrganizationId + - Effect: "Allow" + Action: + - "sqs:SendMessage*" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:${AWS::AccountId}:*" + - Effect: "Allow" + Action: + - "sqs:Ch*" + - "sqs:DeleteM*" + - "sqs:GetQueue*" + - "sqs:Rec*" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:*:*" + - Effect: "Allow" + Action: + - "kinesis:DescribeLimits" + - "kinesis:ListStreams" + Resource: + - "*" + - Effect: "Allow" + Action: + - "kinesis:Desc*" + - "kinesis:Get*" + - "kinesis:PutRecord*" + Resource: + - !Sub "arn:${AWS::Partition}:kinesis:${AWS::Region}:${AWS::AccountId}:stream/*" + - Effect: "Allow" + Action: + - "firehose:PutRecord*" + Resource: + - !Sub "arn:${AWS::Partition}:firehose:${AWS::Region}:${AWS::AccountId}:deliverystream/*" + - Effect: "Allow" + Action: + - "dynamodb:ListStreams" + Resource: + - "*" + - Effect: "Allow" + Action: + - "dynamodb:DescribeStream" + - "dynamodb:GetRecords" + - "dynamodb:GetShardIterator" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*/stream/*" + - Effect: "Allow" + Action: + - "dynamodb:GetItem" + - "dynamodb:Scan" + - "dynamodb:Query" + - "dynamodb:Batch*" + - "dynamodb:Describe*" + - "dynamodb:DeleteItem" + - "dynamodb:PutItem" + - "dynamodb:UpdateItem" + - "dynamodb:PartiQ*" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*" + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*/index/*" + - Effect: "Allow" + Action: + - "s3:ListBucket" + - "s3:ListBucketVersions" + - "s3:ListAllMyBuckets" + - "s3:GetBucketLocation" + - "s3:GetObjec*" + - "s3:GetLifecycleConfiguration" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::*" + - !Sub "arn:${AWS::Partition}:s3:::*/*" + - Effect: "Allow" + Action: + - "s3:DeleteObject*" + - "s3:PutObjec*" + - "s3:PutLifecycle*" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::*" + - !Sub "arn:${AWS::Partition}:s3:::*/*" + Condition: + StringEquals: + "s3:ResourceAccount": + - !Sub "${AWS::AccountId}" + - Effect: "Allow" + Action: + - "events:PutEvents" + Resource: + - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:event-bus/*" + - Effect: "Allow" + Action: + - "ssm:DescribeParameters" + Resource: + - "*" + - Effect: "Allow" + Action: + - "ssm:GetParameter*" + - "ssm:PutParameter" + Resource: + - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*" + - Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:*crypt*" + - "kms:GenerateDataKey*" + - "kms:GetPublicKey" + - "kms:Sign" + - "kms:Verify" + Resource: + - !Sub "arn:${AWS::Partition}:kms:${AWS::Region}:*:key/*" + - Effect: "Allow" + Action: + - "kms:GenerateRandom" + - "kms:ListKeys" + Resource: + - "*" + - Effect: "Allow" + Action: + - "ecr:BatchCheck*" + - "ecr:BatchGetIm*" + - "ecr:GetD*" + Resource: + - !Sub "arn:${AWS::Partition}:ecr:${AWS::Region}:*:repository/*" + - Effect: "Allow" + Action: + - "ecr:GetAuth*" + Resource: + - "*" + - Effect: "Allow" + Action: + - "ecs:RunTask" + Resource: + - !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/*" + - Effect: "Allow" + Action: + - "iam:PassRole" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*" + - !If + - SigningProfile + - Effect: "Allow" + Action: + - "lambda:GetAlias" + - "lambda:GetProvisionedConcurrencyConfig" + - "lambda:UpdateAlias" + - "lambda:InvokeFunction" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" + - !Ref AWS::NoValue + - !If + - InvokableLambda + - Effect: "Allow" + Action: + - "lambda:InvokeFunction" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:*:function:*" + Condition: + ForAnyValue:StringEquals: + "aws:ResourceAccount": !Ref InvokableLambdaAccounts + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "codedeploy:PutLifecycleEventHookExecutionStatus" + Resource: + - !Sub 'arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:*' + - Effect: "Allow" + Action: + - "cloudwatch:DescribeAlarms" + Resource: + - "*" + - Effect: "Allow" + Action: + - "xray:PutTraceSegments" + - "xray:PutTelemetryRecords" + - "xray:GetSampling*" + Resource: + - "*" + - Effect: "Allow" + Action: + - "ec2:Describe*" + Resource: + - "*" + - Effect: "Allow" + Action: + - "ec2:*NetworkInterface" + Resource: + - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:*/*" + - Effect: "Allow" + Action: + - "states:Start*" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:*" + - Effect: "Allow" + Action: + - "states:DescribeExecution" + - "states:StopExecution" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:execution:*:*" + - Effect: "Allow" + Action: + - "states:GetActivityTask" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:activity:*" + - Effect: "Allow" + Action: + - "states:SendTask*" + Resource: + - "*" + - Effect: "Allow" + Action: + - "cognito-idp:Describe*" + Resource: + - !Sub "arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*" + - Effect: "Allow" + Action: + - "glue:GetSecurityConfiguration" + Resource: "*" + - Effect: "Allow" + Action: + - "glue:GetDatabase" + - "glue:GetTable" + - "glue:CreateTa*" + - "glue:CreateDatab*" + - "glue:BatchGetP*" + - "glue:BatchC*" + Resource: + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:*" + - Effect: "Allow" + Action: + - "athena:StartQueryExecution" + - "athena:GetQuery*" + - "athena:*agResource" + Resource: + - !Sub "arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:workgroup/*" + - Effect: "Allow" + Action: + - "s3:CreateJob" + Resource: + - "*" + - Effect: "Allow" + Action: + - "cloudformation:ListStackResources" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/*/*" + - Effect: "Allow" + Action: + - "codebuild:*Projects" + - "codebuild:BatchGetBuild*" + - "codebuild:ListBuild*" + - "codebuild:StartBuild*" + - "codebuild:StopBuild*" + Resource: + - "*" + Condition: + BoolIfExists: + aws:MultiFactorAuthPresent: true + - Effect: Allow + Action: + - "textract:*" + Resource: + - "*" + Condition: + StringEquals: + "aws:PrincipalOrgID": !Ref AWSOrganizationId + - Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" # pragma: allowlist secret + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:216552277552:secret:*" # pragma: allowlist secret + + AppProgrammaticPermissionsBoundary: + Type: AWS::IAM::ManagedPolicy + Properties: + # checkov:skip=CKV_AWS_111:states:Send* are write actions but do not take a resource + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "AppProgrammaticPermissionsBoundary" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - !If + - IncludeAthenaAndGlue + - Effect: "Allow" + Action: + - "athena:StartQueryExecution" + - "athena:GetQueryExecution" + - "athena:GetQueryResults" + - "athena:*TagResource" + Resource: + - !Sub "arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:workgroup/*" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "cloudformation:ListStackResources" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/*/*" + - !If + - IncludeDynamicResources + - Effect: Allow + Action: + - cloudformation:CreateStack + - cloudformation:DeleteStack + - cloudformation:UpdateStack + - cloudformation:CreateChangeSet + Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${DynamicResourcesPrefix}-*" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "cloudwatch:DescribeAlarms" + - "cloudwatch:DeleteAlarms" + Resource: + - "*" + - Effect: "Allow" + Action: + - "cloudwatch:PutMetricData" + - "cloudwatch:PutMetricAlarm" + Resource: + - "*" + - Effect: "Allow" + Action: + - "codebuild:*Projects" + - "codebuild:BatchGetBuild*" + - "codebuild:ListBuild*" + - "codebuild:StartBuild*" + - "codebuild:StopBuild*" + Resource: + - "*" + Condition: + BoolIfExists: + aws:MultiFactorAuthPresent: true + - Effect: "Allow" + Action: + - "codedeploy:PutLifecycleEventHookExecutionStatus" + Resource: + - !Sub 'arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:*' + - !If + - DeployECSCanaryStack + - Effect: "Allow" + Action: + - "cloudformation:DescribeStacks" + - "codedeploy:CreateDeploymentGroup" + - "codedeploy:UpdateDeploymentGroup" + - "codedeploy:DeleteDeploymentGroup" + - "codedeploy:GetDeploymentGroup" + - "codedeploy:GetDeployment" + - "codedeploy:CreateDeployment" + - "codedeploy:GetDeploymentConfig" + - "codedeploy:RegisterApplicationRevision" + - "codedeploy:GetApplicationRevision" + - "ecs:DescribeServices" + - "ecs:UpdateServicePrimaryTaskSet" + - "ecs:DeleteTaskSet" + - "elasticloadbalancing:ModifyListener" + - "elasticloadbalancing:ModifyRule" + - "events:PutRule" + - "events:DeleteRule" + - "events:PutTargets" + - "events:RemoveTargets" + - "lambda:AddPermission" + - "lambda:RemovePermission" + Resource: + - !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/*' + - !Sub 'arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:*' + - !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:*" + - !Sub "arn:${AWS::Partition}:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" + - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:rule/*" + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" + - !Ref AWS::NoValue + - !If + - DeployECSCanaryStack + - Effect: "Allow" + Action: + - "ecs:CreateTaskSet" + - "elasticloadbalancing:DescribeTargetGroups" + - "elasticloadbalancing:DescribeListeners" + - "elasticloadbalancing:DescribeRules" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeCognito + - Effect: "Allow" + Action: + - "cognito-idp:DescribeUserPoolClient" + - "cognito-idp:DeleteUserPoolClient" + - "cognito-idp:Admin*User" + - "cognito-idp:AdminInitiateAuth" + - "cognito-idp:AdminRespondToAuthChallenge" + - "cognito-idp:AdminSetUserMFAPreference" + - "cognito-idp:AdminUpdateUserAttributes" + - "cognito-idp:ChangePassword" + - "cognito-idp:GetLogDeliveryConfiguration" + - "cognito-idp:GetUserAttributeVerificationCode" + - "cognito-idp:RespondToAuthChallenge" + - "cognito-idp:SetLogDeliveryConfiguration" + - "cognito-idp:VerifyUserAttribute" + - "cognito-idp:*ForgotPassword" + - "cognito-idp:UpdateUserPoolClient" + Resource: + - !Sub "arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/*" + - !Ref AWS::NoValue + - !If + - IncludeCognito + - Effect: "Allow" + Action: + - "sns:Publish" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeDynamoDB + - Effect: "Allow" + Action: + - "dynamodb:DescribeStream" + - "dynamodb:GetRecords" + - "dynamodb:GetShardIterator" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*/stream/*" + - !Ref AWS::NoValue + - !If + - IncludeDynamoDB + - Effect: "Allow" + Action: + - "dynamodb:GetItem" + - "dynamodb:Scan" + - "dynamodb:Query" + - "dynamodb:Batch*" + - "dynamodb:DescribeTable" + - "dynamodb:DescribeTimeToLive" + - "dynamodb:DeleteItem" + - "dynamodb:PutItem" + - "dynamodb:UpdateItem" + - "dynamodb:PartiQ*" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*" + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*/index/*" + - !Ref AWS::NoValue + - !If + - IncludeDynamoDB + - Effect: "Allow" + Action: + - "dynamodb:ListStreams" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeDynamoDB + - !If + - AccessDynamoTable + - Effect: "Allow" + Action: + - "dynamodb:BatchGetItem" + - "dynamodb:DescribeTable" + - "dynamodb:Get*" + - "dynamodb:Query" + - "dynamodb:Scan" + - "dynamodb:UpdateItem" + - "dynamodb:PutItem" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:*:table:*" + Condition: + ForAnyValue:StringEquals: + "aws:ResourceAccount": !Ref AccessDynamoDBAccounts #AWS account ID of the resource being accessed + - !Ref AWS::NoValue + - !Ref AWS::NoValue + - !If + - IncludeECRAndECS + - Effect: "Allow" + Action: + - "ecr:BatchCheckLayerAvailability" + - "ecr:BatchGetImage" + - "ecr:GetDownloadUrlForLayer" + Resource: + - !Sub "arn:${AWS::Partition}:ecr:${AWS::Region}:*:repository/*" + - !Ref AWS::NoValue + - !If + - IncludeECRAndECS + - Effect: "Allow" + Action: + - "ecr:GetAuthorizationToken" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeECRAndECS + - Effect: "Allow" + Action: + - "ecs:RunTask" + Resource: + - !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/*" + - !Ref AWS::NoValue + - !If + - IncludeEC2 + - Effect: "Allow" + Action: + - "ec2:DescribeNetworkInterfaces" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeEC2 + - Effect: "Allow" + Action: + - "ec2:*NetworkInterface" + Resource: + - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:*/*" + - !Ref AWS::NoValue + - !If + - IncludeEC2 + - Effect: "Allow" + Action: + - "ec2:StartInstances" + - "ec2:StopInstances" + Resource: + - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*" + - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:license-configuration:*" + Condition: + StringLike: + "ec2:ResourceTag/Name": "*redshift*" + - !Ref AWS::NoValue + - !If + - IncludeEventBridge + - Effect: "Allow" + Action: + - "events:PutEvents" + Resource: + - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:event-bus/*" + - !Ref AWS::NoValue + - !If + - IncludeEventBridge + - Effect: "Allow" + Action: + - "scheduler:UpdateSchedule" + Resource: + - !Sub "arn:${AWS::Partition}:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/*/*" + - !Ref AWS::NoValue + - !If + - IncludeFirehoseAndKinesis + - Effect: "Allow" + Action: + - "firehose:PutRecord*" + - "firehose:DescribeDeliveryStream" + Resource: + - !Sub "arn:${AWS::Partition}:firehose:${AWS::Region}:${AWS::AccountId}:deliverystream/*" + - !Ref AWS::NoValue + - !If + - IncludeAthenaAndGlue + - Effect: "Allow" + Action: + - "glue:GetDatabase" + - "glue:GetTable*" + - "glue:CreateTable" + - "glue:UpdateTable" + - "glue:CreateDatabase" + - "glue:BatchCreatePartition" + - "glue:*GetPartition*" + Resource: + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:*" + - !Ref AWS::NoValue + - !If + - IncludeAthenaAndGlue + - Effect: "Allow" + Action: + - "glue:GetSecurityConfiguration" + Resource: "*" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "iam:PassRole" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*" + - !If + - IncludeFirehoseAndKinesis + - Effect: "Allow" + Action: + - "kinesis:DescribeLimits" + - "kinesis:ListStreams" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeFirehoseAndKinesis + - Effect: "Allow" + Action: + - "kinesis:DescribeStream" + - "kinesis:DescribeStreamSummary" + - "kinesis:Get*" + - "kinesis:PutRecord*" + Resource: + - !Sub "arn:${AWS::Partition}:kinesis:${AWS::Region}:${AWS::AccountId}:stream/*" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:*crypt*" + - "kms:GenerateDataKey*" + - "kms:GetPublicKey" + - "kms:Sign" + - "kms:Verify" + Resource: + - !Sub "arn:${AWS::Partition}:kms:${AWS::Region}:*:key/*" + - Effect: "Allow" + Action: + - "kms:GenerateRandom" + - "kms:ListKeys" + Resource: + - "*" + - !If + - IncludeLambda + - !If + - SigningProfile + - Effect: "Allow" + Action: + - "lambda:GetAlias" + - "lambda:GetProvisionedConcurrencyConfig" + - "lambda:UpdateAlias" + - "lambda:InvokeFunction" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" + - !Ref AWS::NoValue + - !Ref AWS::NoValue + - !If + - IncludeLambda + - !If + - InvokableLambda + - Effect: "Allow" + Action: + - "lambda:InvokeFunction" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:*:function:*" + Condition: + ForAnyValue:StringEquals: + "aws:ResourceAccount": !Ref InvokableLambdaAccounts + - !Ref AWS::NoValue + - !Ref AWS::NoValue + - !If + - IncludeLambda + - Effect: "Allow" + Action: + - "lambda:*EventSourceMapping*" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludePerformanceTest + - !If + - InvokableLambda + - Effect: "Allow" + Action: + - "lambda:InvokeFunction" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:*:function:*" + Condition: + ForAnyValue:StringEquals: + "aws:ResourceAccount": !Ref InvokableLambdaAccounts + - !Ref AWS::NoValue + - !Ref AWS::NoValue + - !If + - IncludePerformanceTest + - Effect: Allow + Action: + - ec2:CreateNetworkInterface + - ec2:DescribeDhcpOptions + - ec2:DescribeNetworkInterfaces + - ec2:DeleteNetworkInterface + - ec2:DescribeSubnets + - ec2:DescribeSecurityGroups + - ec2:DescribeVpcs + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludePerformanceTest + - Effect: Allow + Action: + - ec2:CreateNetworkInterfacePermission + Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*" + Condition: + ArnEquals: + ec2:Subnet: + - Fn::Sub: + - "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${SubnetId}" + - SubnetId: + Fn::ImportValue: + Fn::Sub: "${VpcStackName}-ProtectedSubnetIdA" + - Fn::Sub: + - "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${SubnetId}" + - SubnetId: + Fn::ImportValue: + Fn::Sub: "${VpcStackName}-ProtectedSubnetIdB" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "logs:CreateLog*" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - Effect: "Allow" + Action: + - "logs:PutLogEvents" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:log-stream:*" + - Effect: "Allow" + Action: + - "logs:*LogDeliver*" + - "logs:*LogEvents" + - "logs:PutResourcePolicy" + - "logs:DescribeResourcePolicies" + - "logs:DescribeLog*" + Resource: + - "*" + - Effect: "Allow" + Action: + - "secretsmanager:CreateSecret" # pragma: allowlist secret + - "secretsmanager:DeleteResourcePolicy" # pragma: allowlist secret + - "secretsmanager:DeleteSecret" # pragma: allowlist secret + - "secretsmanager:DescribeSecret" # pragma: allowlist secret + - "secretsmanager:GetRandomPassword" # pragma: allowlist secret + - "secretsmanager:GetResourcePolicy" # pragma: allowlist secret + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecretVersionIds" # pragma: allowlist secret + - "secretsmanager:PutResourcePolicy" # pragma: allowlist secret + - "secretsmanager:PutSecretValue" # pragma: allowlist secret + - "secretsmanager:RestoreSecret" # pragma: allowlist secret + - "secretsmanager:TagResource" # pragma: allowlist secret + - "secretsmanager:UntagResource" # pragma: allowlist secret + - "secretsmanager:UpdateSecret" # pragma: allowlist secret + - "secretsmanager:UpdateSecretVersionStage" # pragma: allowlist secret + - "secretsmanager:ValidateResourcePolicy" # pragma: allowlist secret + Resource: + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" # pragma: allowlist secret + - Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:216552277552:secret:*" # pragma: allowlist secret + - !If + - IncludeQuickSight + - Effect: "Allow" + Action: + - "quicksight:CreateAnalysis" + - "quicksight:CreateGroupMembership" + - "quicksight:DeleteUser" + - "quicksight:DescribeAnalysis" + - "quicksight:DescribeAnalysisPermissions" + - "quicksight:DescribeAssetBundleExportJob" + - "quicksight:DescribeAssetBundleImportJob" + - "quicksight:DescribeTheme" + - "quicksight:DescribeUser" + - "quicksight:GenerateEmbedUrlForRegisteredUser" + - "quicksight:ListGroups" + - "quicksight:ListUserGroups" + - "quicksight:PassDataSet" + - "quicksight:RegisterUser" + - "quicksight:StartAssetBundleExportJob" + - "quicksight:StartAssetBundleImportJob" + - "quicksight:UpdateAnalysis" + Resource: + - !Sub "arn:${AWS::Partition}:quicksight:${AWS::Region}:${AWS::AccountId}:*" + - !Ref AWS::NoValue + - !If + - IncludeAthenaGlueRedshift + - Effect: "Allow" + Action: + - "redshift-data:ExecuteStatement" + Resource: + - !Sub "arn:${AWS::Partition}:redshift:${AWS::Region}:${AWS::AccountId}:*" + - !Sub "arn:${AWS::Partition}:redshift-serverless:${AWS::Region}:${AWS::AccountId}:*" + - !Ref AWS::NoValue + - !If + - IncludeAthenaGlueRedshift + - Effect: "Allow" + Action: + - "redshift-data:DescribeStatement" + - "redshift-data:GetStatementResult" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeSNS + - Effect: "Allow" + Action: + - "sns:Publish" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:*" + Condition: + StringEquals: + "aws:PrincipalOrgID": !Ref AWSOrganizationId + - !Ref AWS::NoValue + - !If + - IncludeSSM + - Effect: "Allow" + Action: + - "ssm:DescribeParameters" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeSSM + - Effect: "Allow" + Action: + - "ssm:GetParameter*" + - "ssm:PutParameter" + Resource: + - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*" + - !Ref AWS::NoValue + - !If + - IncludeStepFunctions + - Effect: "Allow" + Action: + - "states:Start*" + - "states:ListExecutions" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:*" + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:mapRun:*/*:*" + - !Ref AWS::NoValue + - !If + - IncludeStepFunctions + - Effect: "Allow" + Action: + - "states:DescribeExecution" + - "states:StopExecution" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:execution:*:*" + - !Ref AWS::NoValue + - !If + - IncludeStepFunctions + - Effect: "Allow" + Action: + - "states:GetActivityTask" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:activity:*" + - !Ref AWS::NoValue + - !If + - IncludeStepFunctions + - Effect: "Allow" + Action: + - "states:SendTask*" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - IncludeSQS + - Effect: "Allow" + Action: + - "sqs:ChangeMessageVisibility" + - "sqs:DeleteMessage" + - "sqs:GetQueue*" + - "sqs:ReceiveMessage" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:*:*" + - !Ref AWS::NoValue + - !If + - IncludeSQS + - Effect: "Allow" + Action: + - "sqs:SendMessage*" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:${AWS::AccountId}:*" + - !Ref AWS::NoValue + - !If + - IncludePerformanceTest + - !If + - InvokableLambda + - Effect: "Allow" + Action: + - "sqs:SendMessage" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:*:*" + Condition: + ForAnyValue:StringEquals: + "aws:ResourceAccount": !Ref InvokableLambdaAccounts + - !Ref AWS::NoValue + - !Ref AWS::NoValue + - !If + - IncludeDynamicSQS + - Effect: "Allow" + Action: + - "sqs:CreateQueue" + - "sqs:DeleteQueue" + - "sqs:SetQueueAttributes" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:${AWS::AccountId}:${DynamicResourcesPrefix}-*" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "sts:AssumeRole" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${SAMStackName}-PerformanceTesterRole" + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${SAMStackName}-*pairwise*" + - Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:ListBucket" + - "s3:ListBucketVersions" + - "s3:ListAllMyBuckets" + - "s3:GetBucketLocation" + - "s3:GetObjectTagging" + - "s3:GetObjectVersion" + - "s3:GetLifecycleConfiguration" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::*" + - !Sub "arn:${AWS::Partition}:s3:::*/*" + - Effect: "Allow" + Action: + - "s3:DeleteObject*" + - "s3:PutObject" + - "s3:PutObjectAcl" + - "s3:PutObjectTagging" + - "s3:PutLifecycleConfiguration" + - "s3:RestoreObject" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::*" + - !Sub "arn:${AWS::Partition}:s3:::*/*" + Condition: + StringEquals: + "s3:ResourceAccount": + - !Sub "${AWS::AccountId}" + - Effect: "Allow" + Action: + - "s3:CreateJob" + - "s3:DescribeJob" + - "s3:*JobTagging" # GetJobTagging, PutJobTagging, DeleteJobTagging + Resource: + - "*" + - Effect: Allow + Action: + - "textract:*" + Resource: + - "*" + Condition: + StringEquals: + "aws:PrincipalOrgID": !Ref AWSOrganizationId + - !If + - IncludeXray + - Effect: "Allow" + Action: + - "xray:PutTraceSegments" + - "xray:PutTelemetryRecords" + - "xray:GetSampling*" + Resource: + - "*" + - !Ref AWS::NoValue + + DeployPolicy1: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy1" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "PolicyManagement" + Effect: "Allow" + Action: + - "iam:CreatePolicy*" + - "iam:DeletePolicy*" + - "iam:GetPolicy*" + - "iam:ListPolicyTags" + - "iam:ListPolicyVersions" + - "iam:TagPolicy" + - "iam:UntagPolicy" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/*" + - !If + - UseProgrammaticPermissionsBoundary + - Sid: "RoleManagementWithBoundary" + Effect: "Allow" + Action: + - "iam:AttachRolePolicy" + - "iam:CreateRole" + - "iam:DeleteRolePolicy" + - "iam:DetachRolePolicy" + - "iam:PutRolePermissionsBoundary" + - "iam:PutRolePolicy" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${SAMStackName}-*" + Condition: + StringEquals: + "iam:PermissionsBoundary": !Ref AppProgrammaticPermissionsBoundary + - Sid: "RoleManagementWithBoundary" + Effect: "Allow" + Action: + - "iam:AttachRolePolicy" + - "iam:CreateRole" + - "iam:DeleteRolePolicy" + - "iam:DetachRolePolicy" + - "iam:PutRolePermissionsBoundary" + - "iam:PutRolePolicy" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${SAMStackName}-*" + Condition: + StringEquals: + "iam:PermissionsBoundary": !Ref AppPermissionsBoundary + - Sid: "RoleManagement" + Effect: "Allow" + Action: + - "iam:DeleteRole" + - "iam:GetRole" + - "iam:GetRolePolicy" + - "iam:ListRolePolicies" + - "iam:ListAttachedRolePolicies" + - "iam:ListRoleTags" + - "iam:TagRole" + - "iam:UntagRole" + - "iam:UpdateAssumeRolePolicy" + - "iam:UpdateRole" + - "iam:UpdateRoleDescription" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${SAMStackName}-*" + - Sid: "ServiceLinkedRoleManagement" + Effect: "Allow" + Action: + - "iam:CreateServiceLinkedRole" + - "iam:DeleteServiceLinkedRole" + - "iam:GetServiceLinkedRoleDeletionStatus" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/*" + - Sid: "ListServiceRoles" + Effect: "Allow" + Action: + - "iam:ListRoles" + Resource: "*" + - Sid: "PassRole" + Effect: "Allow" + Action: + - "iam:PassRole" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${SAMStackName}-*" + - Sid: "PassAutoscalingRole" + Effect: "Allow" + Action: + - "iam:PassRole" + Resource: + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/lambda.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency" + - !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService" + - !If + - SigningProfile + - Sid: "DeploySignedLambda" + Effect: "Allow" + Action: + - "lambda:CreateFunction" + - "lambda:PutFunctionCodeSigningConfig" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" + Condition: + ForAnyValue:StringEquals: + lambda:CodeSigningConfigArn: + - !Ref CodeSigningConfig + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "CodeSigning" + Effect: "Allow" + Action: + - "lambda:GetCodeSigningConfig" + Resource: + - !Ref CodeSigningConfig + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "ManageLambda" + Effect: "Allow" + Action: + - "lambda:AddPermission" + - "lambda:CreateAlias" + - "lambda:DeleteAlias" + - "lambda:DeleteFunction" + - "lambda:DeleteFunctionConcurrency" + - "lambda:DeleteFunctionEventInvokeConfig" + - "lambda:GetAlias" + - "lambda:GetFunction" + - "lambda:GetFunctionConfiguration" + - "lambda:GetFunctionEventInvokeConfig" + - "lambda:GetPolicy" + - "lambda:InvokeAsync" + - "lambda:InvokeFunction" + - "lambda:ListAliases" + - "lambda:ListFunctionEventInvokeConfigs" + - "lambda:ListProvisionedConcurrencyConfigs" + - "lambda:ListTags" + - "lambda:ListVersionsByFunction" + - "lambda:PublishVersion" + - "lambda:PutFunctionConcurrency" + - "lambda:PutFunctionEventInvokeConfig" + - "lambda:RemovePermission" + - "lambda:TagResource" + - "lambda:UntagResource" + - "lambda:UpdateAlias" + - "lambda:UpdateFunctionCode" + - "lambda:UpdateFunctionConfiguration" + - "lambda:UpdateFunctionEventInvokeConfig" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "ManageLambdaLayers" + Effect: "Allow" + Action: + - "lambda:AddLayerVersionPermission" + - "lambda:DeleteLayerVersion" + - "lambda:GetLayerVersion*" + - "lambda:PublishLayerVersion" + - "lambda:RemoveLayerVersionPermission" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:*" + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:layer:*:*" + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "LambdaLayers" + Effect: "Allow" + Action: + - "lambda:ListLayer*" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "EventSourceMappings" + Effect: "Allow" + Action: + - "lambda:CreateEventSourceMapping" + - "lambda:GetEventSourceMapping" + - "lambda:ListEventSourceMappings" + Resource: + - "*" + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "EventSourceMappingManagement" + Effect: "Allow" + Action: + - "lambda:*EventSourceMapping" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:event-source-mapping:*" + - !Ref AWS::NoValue + - !If + - SigningProfile + - Sid: "ProvisionedConcurrency" + Effect: "Allow" + Action: + - "lambda:DeleteProvisionedConcurrencyConfig" + - "lambda:GetProvisionedConcurrencyConfig" + - "lambda:PutProvisionedConcurrencyConfig" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*:*" + - !Ref AWS::NoValue + - Sid: "ManageAutoscaling" + Effect: "Allow" + Action: + - "application-autoscaling:*ScalableTarge*" + - "application-autoscaling:DescribeScalingPolicies" + - "application-autoscaling:PutScalingPolicy" + - "application-autoscaling:DeleteScalingPolicy" + - "application-autoscaling:DescribeScheduledActions" + Resource: "*" + - Sid: "AthenaWorkGroup" + Effect: "Allow" + Action: + - "athena:CreateWorkGroup" + - "athena:DeleteWorkGroup" + - "athena:GetWorkGroup" + - "athena:ListWorkGroups" + - "athena:UpdateWorkGroup" + - "athena:*agResourc*" + Resource: + - !Sub "arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:workgroup/*" + + DeployPolicy2: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy2" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "ApiGateway" + Effect: "Allow" + Action: + - "apigateway:*" + Resource: + - !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}::*" + - Sid: "SNS" + Effect: "Allow" + Action: + - "sns:GetSubscriptionAttributes" + - "sns:ListSubscriptions" + - "sns:ListTopics" + - "sns:SetSubscriptionAttributes" + Resource: + - "*" + - Sid: "SNSSubscribe" + Effect: "Allow" + Action: + - "sns:Unsubscribe" + - "sns:Subscribe" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:*:*" + - Sid: "SNSManagement" + Effect: "Allow" + Action: + - "sns:AddPermission" + - "sns:CreateTopic" + - "sns:DeleteTopic" + - "sns:GetTopicAttributes" + - "sns:ListSubscriptionsByTopic" + - "sns:ListTagsForResource" + - "sns:Publish" + - "sns:RemovePermission" + - "sns:SetTopicAttributes" + - "sns:TagResource" + - "sns:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "SQSManagement" + Effect: "Allow" + Action: + - "sqs:AddPermission" + - "sqs:ChangeMessageVisibility" + - "sqs:CreateQueue" + - "sqs:DeleteMessage" + - "sqs:DeleteQueue" + - "sqs:GetQueueAttributes" + - "sqs:ListDeadLetterSourceQueues" + - "sqs:ListQueueTags" + - "sqs:PurgeQueue" + - "sqs:ReceiveMessage" + - "sqs:RemovePermission" + - "sqs:SendMessage" + - "sqs:SetQueueAttributes" + - "sqs:TagQueue" + - "sqs:UntagQueue" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "SQS" + Effect: "Allow" + Action: + - "sqs:ListQueues" + Resource: + - "*" + - Sid: "KinesisStreamManagement" + Effect: "Allow" + Action: + - "kinesis:AddTagsToStream" + - "kinesis:CreateStream" + - "kinesis:DecreaseStreamRetentionPeriod" + - "kinesis:DeleteStream" + - "kinesis:DescribeStream*" + - "kinesis:GetRecords" + - "kinesis:GetShardIterator" + - "kinesis:IncreaseStreamRetentionPeriod" + - "kinesis:ListStreamConsumers" + - "kinesis:ListTagsForStream" + - "kinesis:MergeShards" + - "kinesis:PutRecord*" + - "kinesis:RegisterStreamConsumer" + - "kinesis:RemoveTagsFromStream" + - "kinesis:SplitShard" + - "kinesis:*StreamEncryption" + - "kinesis:SubscribeToShard" + Resource: + - !Sub "arn:${AWS::Partition}:kinesis:${AWS::Region}:${AWS::AccountId}:stream/*" + - Sid: "Kinesis" + Effect: "Allow" + Action: + - "kinesis:*EnhancedMonitoring" + - "kinesis:ListShards" + - "kinesis:ListStreams" + - "kinesis:UpdateShardCount" + - "kinesis:UpdateStreamMode" + Resource: + - "*" + - Sid: "KinesisFirehoseManagement" + Effect: "Allow" + Action: + - "firehose:CreateDeliveryStream" + - "firehose:DeleteDeliveryStream" + - "firehose:DescribeDeliveryStream" + - "firehose:ListTagsForDeliveryStream" + - "firehose:PutRecord*" + - "firehose:StartDeliveryStreamEncryption" + - "firehose:StopDeliveryStreamEncryption" + - "firehose:TagDeliveryStream" + - "firehose:UntagDeliveryStream" + - "firehose:UpdateDestination" + Resource: + - !Sub "arn:${AWS::Partition}:firehose:${AWS::Region}:${AWS::AccountId}:deliverystream/*" + - Sid: "KinesisFirehose" + Effect: "Allow" + Action: + - "firehose:ListDeliveryStreams" + Resource: + - "*" + - Sid: "DynamoDBManagement" + Effect: "Allow" + Action: + - "dynamodb:Batch*" + - "dynamodb:ConditionCheckItem" + - "dynamodb:CreateBackup" + - "dynamodb:CreateTable*" + - "dynamodb:DeleteTable*" + - "dynamodb:DescribeContinuousBackups" + - "dynamodb:DescribeContributorInsights" + - "dynamodb:DescribeKinesisStreamingDestination" + - "dynamodb:DescribeTable*" + - "dynamodb:DescribeTimeToLive" + - "dynamodb:DisableKinesisStreamingDestination" + - "dynamodb:EnableKinesisStreamingDestination" + - "dynamodb:ExportTableToPointInTime" + - "dynamodb:GetItem" + - "dynamodb:List*" + - "dynamodb:PartiQL*" + - "dynamodb:PutItem" + - "dynamodb:Query" + - "dynamodb:Restore*" + - "dynamodb:Scan" + - "dynamodb:StartAwsBackupJob" + - "dynamodb:TagResource" + - "dynamodb:UntagResource" + - "dynamodb:UpdateContinuousBackups" + - "dynamodb:UpdateContributorInsights" + - "dynamodb:UpdateItem" + - "dynamodb:UpdateTable*" + - "dynamodb:UpdateTimeToLive" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*" + - Sid: "DynamoDBStreamManagement" + Effect: "Allow" + Action: + - "dynamodb:DescribeStream" + - "dynamodb:GetRecords" + - "dynamodb:GetShardIterator" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*/stream/*" + - Sid: "DynamoDB" + Effect: "Allow" + Action: + - "dynamodb:ListBackups" + - "dynamodb:ListContributorInsights" + - "dynamodb:ListStreams" + - "dynamodb:ListTables" + Resource: + - "*" + - Sid: "S3Management" + Effect: "Allow" + Action: + - "s3:CreateBucket" + - "s3:DeleteAccessGrant" + - "s3:DeleteBucket" + - "s3:DeleteBucketPolicy" + - "s3:GetAccelerateConfiguration" + - "s3:GetBucket*" + - "s3:GetEncryptionConfiguration" + - "s3:GetIntelligentTieringConfiguration" + - "s3:GetInventoryConfiguration" + - "s3:GetLifecycleConfiguration" + - "s3:GetMetricsConfiguration" + - "s3:GetObject" + - "s3:GetReplicationConfiguration" + - "s3:ListBucket*" + - "s3:PutAccelerateConfiguration" + - "s3:PutAccountPublicAccessBlock" + - "s3:PutAnalyticsConfiguration" + - "s3:PutBucket*" + - "s3:PutEncryptionConfiguration" + - "s3:PutIntelligentTieringConfiguration" + - "s3:PutInventoryConfiguration" + - "s3:PutLifecycleConfiguration" + - "s3:PutMetricsConfiguration" + - "s3:PutReplicationConfiguration" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::*" + - Sid: "EventBusManagement" + Effect: "Allow" + Action: + - "events:CreateEventBus" + - "events:DeleteEventBus" + - "events:DescribeEventBus" + - "events:ListTagsForResource" + - "events:PutEvents" + - "events:TagResource" + - "events:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:event-bus/*" + - Sid: "EventRuleManagement" + Effect: "Allow" + Action: + - "events:DeleteRule" + - "events:DescribeRule" + - "events:DisableRule" + - "events:EnableRule" + - "events:ListTagsForResource" + - "events:PutRule" + - "events:PutTargets" + - "events:RemoveTargets" + - "events:TagResource" + - "events:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:rule/*" + - Sid: "EventHub" + Effect: "Allow" + Action: + - "events:ListEventBuses" + - "events:ListRules" + - "events:PutPermission" + - "events:RemovePermission" + - "events:TestEventPattern" + Resource: + - "*" + + DeployPolicy3: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy3" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "LogManagement" + Effect: "Allow" + Action: + - "logs:AssociateKmsKey" + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:DeleteLogGroup" + - "logs:DeleteLogStream" + - "logs:DeleteMetricFilter" + - "logs:DeleteRetentionPolicy" + - "logs:DeleteSubscriptionFilter" + - "logs:DescribeLogGroups" + - "logs:DescribeLogStreams" + - "logs:DescribeMetricFilters" + - "logs:DescribeSubscriptionFilters" + - "logs:DisassociateKmsKey" + - "logs:GetLogEvents" + - "logs:FilterLogEvents" + - "logs:ListTagsLogGroup" + - "logs:ListTagsForResource" + - "logs:PutMetricFilter" + - "logs:PutRetentionPolicy" + - "logs:PutSubscriptionFilter" + - "logs:TagLogGroup" + - "logs:UntagLogGroup" + - "logs:TagResource" + - "logs:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:log-stream:*" + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:885513274347:destination:*" + - Sid: "CloudWatchAlarmManagement" + Effect: "Allow" + Action: + - "cloudwatch:DeleteAlarms" + - "cloudwatch:DescribeAlarmHistory" + - "cloudwatch:DescribeAlarms" + - "cloudwatch:DisableAlarmActions" + - "cloudwatch:EnableAlarmActions" + - "cloudwatch:ListTagsForResource" + - "cloudwatch:PutCompositeAlarm" + - "cloudwatch:PutMetricAlarm" + - "cloudwatch:SetAlarmState" + - "cloudwatch:TagResource" + - "cloudwatch:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:cloudwatch:${AWS::Region}:${AWS::AccountId}:alarm:*" + - Sid: "CloudWatchDashboardManagement" + Effect: "Allow" + Action: + - "cloudwatch:DeleteDashboards" + - "cloudwatch:GetDashboard" + - "cloudwatch:ListTagsForResource" + - "cloudwatch:PutDashboard" + - "cloudwatch:TagResource" + - "cloudwatch:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:cloudwatch::${AWS::AccountId}:dashboard/*" + - Sid: "CloudWatchInsightRuleManagement" + Effect: "Allow" + Action: + - "cloudwatch:DeleteInsightRules" + - "cloudwatch:DisableInsightRules" + - "cloudwatch:EnableInsightRules" + - "cloudwatch:GetInsightRuleReport" + - "cloudwatch:ListTagsForResource" + - "cloudwatch:PutInsightRule" + - "cloudwatch:TagResource" + - "cloudwatch:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:cloudwatch:${AWS::Region}:${AWS::AccountId}:insight-rule/*" + - Sid: "CloudWatch" + Effect: "Allow" + Action: + - "cloudwatch:DeleteAnomalyDetector" + - "cloudwatch:DescribeAlarmsForMetric" + - "cloudwatch:DescribeAnomalyDetectors" + - "cloudwatch:DescribeInsightRules" + - "cloudwatch:ListDashboards" + - "cloudwatch:PutAnomalyDetector" + - "cloudwatch:PutMetricData" + Resource: + - "*" + - Sid: "ApiGatewayLogDelivery" + Effect: "Allow" + Action: + - "logs:CreateLogDelivery" + - "logs:DeleteLogDelivery" + - "logs:DescribeResourcePolicies" + - "logs:GetLogDelivery" + - "logs:ListLogDeliveries" + - "logs:PutResourcePolicy" + - "logs:UpdateLogDelivery" + Resource: + - "*" + - Sid: "SSMParameterManagement" + Effect: "Allow" + Action: + - "ssm:AddTagsToResource" + - "ssm:DeleteParameter*" + - "ssm:DescribeParameters" + - "ssm:GetParameter*" + - "ssm:LabelParameterVersion" + - "ssm:ListTagsForResource" + - "ssm:PutParameter" + - "ssm:RemoveTagsFromResource" + - "ssm:UnlabelParameterVersion" + Resource: + - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/*" + - Sid: "SecretsManagerManagement" + Effect: "Allow" + Action: + - "secretsmanager:CancelRotateSecret" # pragma: allowlist secret + - "secretsmanager:CreateSecret" # pragma: allowlist secret + - "secretsmanager:DeleteResourcePolicy" # pragma: allowlist secret + - "secretsmanager:DeleteSecret" # pragma: allowlist secret + - "secretsmanager:DescribeSecret" # pragma: allowlist secret + - "secretsmanager:GetResourcePolicy" # pragma: allowlist secret + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecretVersionIds" # pragma: allowlist secret + - "secretsmanager:PutResourcePolicy" # pragma: allowlist secret + - "secretsmanager:PutSecretValue" # pragma: allowlist secret + - "secretsmanager:RestoreSecret" # pragma: allowlist secret + - "secretsmanager:RotateSecret" # pragma: allowlist secret + - "secretsmanager:TagResource" # pragma: allowlist secret + - "secretsmanager:UntagResource" # pragma: allowlist secret + - "secretsmanager:UpdateSecret" # pragma: allowlist secret + - "secretsmanager:UpdateSecretVersionStage" # pragma: allowlist secret + - "secretsmanager:ValidateResourcePolicy" # pragma: allowlist secret + Resource: + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" + - Sid: "ObservabilitySecretsManager" + Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: + - "arn:aws:secretsmanager:eu-west-2:216552277552:secret:*" # pragma: allowlist secret + - "arn:aws:secretsmanager:eu-west-2:985486846182:secret:*" # pragma: allowlist secret + - "arn:aws:secretsmanager:eu-west-2:841529299698:secret:*" # pragma: allowlist secret + - Sid: "ObservabilityLambdaLayer" + Effect: "Allow" + Action: + - "lambda:GetLayerVersion" + Resource: + - "arn:aws:lambda:eu-west-2:216552277552:layer:*:*" + - "arn:aws:lambda:eu-west-2:985486846182:layer:*:*" + - "arn:aws:lambda:eu-west-2:841529299698:layer:*:*" + - Sid: "ObservabilityKMS" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + Resource: + - "arn:aws:kms:eu-west-2:216552277552:key/*" + - "arn:aws:kms:eu-west-2:985486846182:key/*" + - "arn:aws:kms:eu-west-2:841529299698:key/*" + - !If + - CreateCustomKMSKeyPolicy + - Sid: "CustomKMSKeySigning" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + Resource: !Ref "CustomKmsKeyArns" + - !Ref AWS::NoValue + - Sid: "SecretsManagerWithoutTiesToResourcesManagement" + Effect: "Allow" + Action: + - "secretsmanager:GetRandomPassword" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: "*" + - Sid: "StepFunctionManagement" + Effect: "Allow" + Action: + - "states:CreateActivity" + - "states:CreateStateMachine" + - "states:DeleteActivity" + - "states:DeleteStateMachine" + - "states:UpdateStateMachine" + - "states:DescribeActivity" + - "states:DescribeStateMachine" + - "states:ListTagsForResource" + - "states:TagResource" + - "states:UntagResource" + - "states:DescribeStateMachineAlias" + - "states:DeleteStateMachineAlias" + - "states:ListStateMachineAliases" + - "states:CreateStateMachineAlias" + - "states:UpdateStateMachineAlias" + - "states:DeleteStateMachineVersion" + - "states:ListStateMachineVersions" + - "states:PublishStateMachineVersion" + Resource: + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:activity:*" + - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:*" + - Sid: "ListStepFunctions" + Effect: "Allow" + Action: + - "states:ListActivities" + - "states:ListStateMachines" + Resource: + - "*" + + DeployPolicy4: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy4" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "CodeDeployApplicationManagement" + Effect: "Allow" + Action: + - "codedeploy:BatchGetApplicationRevisions" + - "codedeploy:BatchGetApplications" + - "codedeploy:CreateApplication" + - "codedeploy:DeleteApplication" + - "codedeploy:GetApplication" + - "codedeploy:GetApplicationRevision" + - "codedeploy:ListApplicationRevisions" + - "codedeploy:ListDeploymentGroups" + - "codedeploy:ListTagsForResource" + - "codedeploy:RegisterApplicationRevision" + - "codedeploy:TagResource" + - "codedeploy:UntagResource" + - "codedeploy:UpdateApplication" + Resource: + - !Sub "arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:application:*" + - Sid: "CodeDeployDeploymentGroupManagement" + Effect: "Allow" + Action: + - "codedeploy:BatchGetDeploymentGroups" + - "codedeploy:BatchGetDeploymentInstances" + - "codedeploy:BatchGetDeployments" + - "codedeploy:CreateDeployment" + - "codedeploy:CreateDeploymentGroup" + - "codedeploy:DeleteDeploymentGroup" + - "codedeploy:GetDeployment" + - "codedeploy:GetDeploymentGroup" + - "codedeploy:GetDeploymentInstance" + - "codedeploy:ListDeploymentInstances" + - "codedeploy:ListTagsForResource" + - "codedeploy:TagResource" + - "codedeploy:UntagResource" + - "codedeploy:UpdateDeploymentGroup" + Resource: + - !Sub "arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:*/*" + - Sid: "CodeDeployDeploymentConfigManagement" + Effect: "Allow" + Action: + - "codedeploy:CreateDeploymentConfig" + - "codedeploy:DeleteDeploymentConfig" + - "codedeploy:GetDeploymentConfig" + Resource: + - !Sub "arn:${AWS::Partition}:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentconfig:*" + - Sid: "CodeDeploy" + Effect: "Allow" + Action: + - "codedeploy:BatchGetDeploymentTargets" + - "codedeploy:ContinueDeployment" + - "codedeploy:CreateCloudFormationDeployment" + - "codedeploy:GetDeploymentTarget" + - "codedeploy:ListApplications" + - "codedeploy:ListDeploymentConfigs" + - "codedeploy:ListDeploymentTargets" + - "codedeploy:SkipWaitTimeForInstanceTermination" + - "codedeploy:StopDeployment" + Resource: + - "*" + - Sid: "KMS" + Effect: "Allow" + Action: + - "kms:CreateKey" + - "kms:GenerateRandom" + - "kms:ListAliases" + - "kms:ListKeys" + Resource: + - "*" + - Sid: "KMSKeyManagement" + Effect: "Allow" + Action: + - "kms:CancelKeyDeletion" + - "kms:CreateGrant" + - "kms:Decrypt" + - "kms:DescribeKey" + - "kms:DisableKey*" + - "kms:EnableKey*" + - "kms:Encrypt" + - "kms:GenerateDataKey*" + - "kms:GetKeyPolicy" + - "kms:GetKeyRotationStatus" + - "kms:GetPublicKey" + - "kms:ListGrants" + - "kms:ListKeyPolicies" + - "kms:ListResourceTags" + - "kms:PutKeyPolicy" + - "kms:ReEncrypt*" + - "kms:RetireGrant" + - "kms:RevokeGrant" + - "kms:ScheduleKeyDeletion" + - "kms:Sign" + - "kms:TagResource" + - "kms:UntagResource" + - "kms:UpdateKeyDescription" + - "kms:Verify" + Resource: + - !Sub "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/*" + - Sid: "KMSAliasManagement" + Effect: "Allow" + Action: + - "kms:CreateAlias" + - "kms:DeleteAlias" + - "kms:UpdateAlias" + Resource: + - !Sub "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:alias/*" + - !Sub "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/*" + - Sid: "AccessSourceArtifacts" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + Resource: + - !If + - CreateGitHubActionsResources + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Ref ArtifactSourceBucketArn + - !If + - CreateGitHubActionsResources + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + - !Sub "${ArtifactSourceBucketArn}/*" + - Sid: "TemplateBucketAccess" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetObjectTagging" + - "s3:ListBucket" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh" + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh/*" + - Sid: "ManageAppStack" + Effect: "Allow" + Action: + - "cloudformation:CreateChangeSet" + - "cloudformation:CreateStack" + - "cloudformation:DeleteStack" + - "cloudformation:DescribeChangeSet" + - "cloudformation:DescribeStack*" + - "cloudformation:DetectStack*" + - "cloudformation:ExecuteChangeSet" + - "cloudformation:GetStackPolicy" + - "cloudformation:GetTemplate" + - "cloudformation:GetTemplateSummary" + - "cloudformation:ListChangeSets" + - "cloudformation:ListStackResources" + - "cloudformation:TagResource" + - "cloudformation:UpdateStack" + - "cloudformation:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${SAMStackName}*" + - Sid: "DescribeCurrentStack" + Effect: "Allow" + Action: + - "cloudformation:DescribeStacks" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*" + - Sid: "ServerlessTransform" + Effect: "Allow" + Action: + - "cloudformation:CreateChangeSet" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:aws:transform/Serverless-2016-10-31" + - Sid: "IncludeTransform" + Effect: "Allow" + Action: + - "cloudformation:CreateChangeSet" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:aws:transform/Include" + - Sid: "CodeDeployBlueGreenTransform" + Effect: "Allow" + Action: + - "cloudformation:CreateChangeSet" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:aws:transform/CodeDeployBlueGreen" + - Sid: "LanguageExtensionsTransform" + Effect: "Allow" + Action: + - "cloudformation:CreateChangeSet" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:aws:transform/LanguageExtensions" + + DeployPolicy5: + Type: AWS::IAM::ManagedPolicy + Properties: + # checkov:skip=CKV_AWS_111:test_create_star + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy5" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "ManageAlb" + Effect: "Allow" + Action: + - "elasticloadbalancing:CreateTargetGroup" + - "elasticloadbalancing:DeleteTargetGroup" + - "elasticloadbalancing:ModifyTargetGroup" + - "elasticloadbalancing:RegisterTargets" + - "elasticloadbalancing:CreateListener" + - "elasticloadbalancing:DeleteListener" + - "elasticloadbalancing:ModifyListener" + - "elasticloadbalancing:CreateRule" + - "elasticloadbalancing:DeleteRule" + - "elasticloadbalancing:DescribeRules" + - "elasticloadbalancing:CreateLoadBalancer" + - "elasticloadbalancing:CreateLoadBalancerListeners" + - "elasticloadbalancing:DeleteLoadBalancerListeners" + - "elasticloadbalancing:DescribeLoadBalancerAttributes" + - "elasticloadbalancing:DescribeLoadBalancerListeners" + - "elasticloadbalancing:DeleteLoadBalancer" + - "elasticloadbalancing:ModifyLoadBalancerAttributes" + - "elasticloadbalancing:ModifyTargetGroupAttributes" + - "elasticloadbalancing:AddTags" + - "elasticloadbalancing:RemoveTags" + - "elasticloadbalancing:ModifyListener" + - "elasticloadbalancing:ModifyLoadBalancerAttributes" + - "elasticloadbalancing:ModifyRule" + - "elasticloadbalancing:SetWebACL" + Resource: + - !Sub "arn:${AWS::Partition}:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "ListAlbResources" + Effect: "Allow" + Action: + - "elasticloadbalancing:DescribeLoadBalancers" + - "elasticloadbalancing:DescribeListeners" + - "elasticloadbalancing:DescribeTargetGroups" + - "elasticloadbalancing:DescribeTargetGroupAttributes" + - "elasticloadbalancing:DescribeTargetHealth" + - "elasticloadbalancing:DescribeTags" + - "elasticloadbalancing:DescribeRules" + Resource: + - "*" + - Sid: "ManageSecurityGroups" + Effect: "Allow" + Action: + - "ec2:CreateTags" + - "ec2:DeleteTags" + - "ec2:CreateSecurityGroup" + - "ec2:DeleteSecurityGroup" + - "ec2:ModifySecurityGroupRules" + - "ec2:AuthorizeSecurityGroupIngress" + - "ec2:AuthorizeSecurityGroupEgress" + - "ec2:RevokeSecurityGroupIngress" + - "ec2:RevokeSecurityGroupEgress" + - "ec2:UpdateSecurityGroupRuleDescriptionsIngress" + - "ec2:UpdateSecurityGroupRuleDescriptionsEgress" + Resource: + - !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:*/*" + - Sid: "VPC" + Effect: "Allow" + Action: + - "ec2:DescribeAccountAttributes" + - "ec2:DescribeAddresses" + - "ec2:DescribeInternetGateways" + - "ec2:DescribeNatGateways" + - "ec2:DescribeSecurityGroups" + - "ec2:DescribeSubnets" + - "ec2:DescribeVpcs" + Resource: + - "*" + - Sid: "CreateEcsClusters" + Effect: "Allow" + Action: + - "ecs:CreateCluster" + - "ecs:DescribeClusters" + - "ecs:DescribeServices" + Resource: + - "*" + - Sid: "ManageEcs" + Effect: "Allow" + Action: + - "ecs:DeleteCluster" + - "ecs:UpdateCluster" + - "ecs:CreateService" + - "ecs:DeleteService" + - "ecs:UpdateService" + - "ecs:TagResource" + - "ecs:UntagResource" + - "ecs:ListTagsForResource" + Resource: + - !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "ManageEcsTaskDefinitions" + Effect: "Allow" + Action: + - "ecs:RegisterTaskDefinition" + - "ecs:DeregisterTaskDefinition" + - "ecs:DescribeTaskDefinition" + - "ecs:CreateTaskSet" + - "ecs:DeleteTaskSet" + - "ecs:DescribeTaskSets" + - "ecs:UpdateServicePrimaryTaskSet" + - "ecs:UpdateTaskSet" + Resource: + - "*" + - Sid: "CreateRoute53HostedZones" + Effect: "Allow" + Action: + - "route53:CreateHostedZone" + Resource: "*" + - Sid: "ManageRoute53" + Effect: "Allow" + Action: + - "route53:GetHostedZone" + - "route53:DeleteHostedZone" + - "route53:UpdateHostedZoneComment" + - "route53:ChangeResourceRecordSets" + - "route53:ListResourceRecordSets" + - "route53:ChangeTagsForResource" + - "route53:ListTagsForResource" + - "route53:GetChange" + - "route53:ListQueryLoggingConfigs" + Resource: + - !Sub "arn:${AWS::Partition}:route53:::hostedzone/*" + - !Sub "arn:${AWS::Partition}:route53:::change/*" + - Sid: "CreateCertificates" + Effect: "Allow" + Action: + - "acm:RequestCertificate" + Resource: "*" + - Sid: "ManageCertificates" + Effect: "Allow" + Action: + - "acm:DescribeCertificate" + - "acm:DeleteCertificate" + - "acm:ListTagsForCertificate" + - "acm:AddTagsToCertificate" + - "acm:RemoveTagsFromCertificate" + - "acm:UpdateCertificateOptions" + Resource: !Sub "arn:${AWS::Partition}:acm:${AWS::Region}:${AWS::AccountId}:certificate/*" + - Sid: "ElastiCacheManagement" + Effect: "Allow" + Action: + - "elasticache:AddTagsToResource" + - "elasticache:AuthorizeCacheSecurityGroupIngress" + - "elasticache:CreateCacheCluster" + - "elasticache:CreateCacheParameterGroup" + - "elasticache:CreateCacheSecurityGroup" + - "elasticache:CreateCacheSubnetGroup" + - "elasticache:CreateReplicationGroup" + - "elasticache:DecreaseReplicaCount" + - "elasticache:DeleteCacheCluster" + - "elasticache:DeleteCacheParameterGroup" + - "elasticache:DeleteReplicationGroup" + - "elasticache:DeleteCacheSecurityGroup" + - "elasticache:DeleteCacheSubnetGroup" + - "elasticache:DescribeCacheClusters" + - "elasticache:DescribeCacheParameterGroups" + - "elasticache:DescribeCacheParameters" + - "elasticache:DescribeCacheParameterGroups" + - "elasticache:DescribeReplicationGroups" + - "elasticache:DescribeCacheSubnetGroups" + - "elasticache:IncreaseReplicaCount" + - "elasticache:ListAllowedNodeTypeModifications" + - "elasticache:ListTagsForResource" + - "elasticache:ModifyCacheCluster" + - "elasticache:ModifyCacheParameterGroup" + - "elasticache:ModifyCacheSubnetGroup" + - "elasticache:ModifyReplicationGroup" + - "elasticache:ModifyReplicationGroupShardConfiguration" + - "elasticache:RemoveTagsFromResource" + - "elasticache:ResetCacheParameterGroup" + - "elasticache:RevokeCacheSecurityGroupIngress" + Resource: + - !Sub "arn:${AWS::Partition}:elasticache:${AWS::Region}:${AWS::AccountId}:cluster:*" + - !Sub "arn:${AWS::Partition}:elasticache:${AWS::Region}:${AWS::AccountId}:parametergroup:*" + - !Sub "arn:${AWS::Partition}:elasticache:${AWS::Region}:${AWS::AccountId}:replicationgroup:*" + - !Sub "arn:${AWS::Partition}:elasticache:${AWS::Region}:${AWS::AccountId}:securitygroup:*" + - !Sub "arn:${AWS::Partition}:elasticache:${AWS::Region}:${AWS::AccountId}:subnetgroup:*" + - Sid: "ElastiCache" + Effect: "Allow" + Action: + - "elasticache:DescribeCacheEngineVersions" + - "elasticache:DescribeEngineDefaultParameters" + Resource: + - "*" + - Sid: "SESPermissions" + Effect: "Allow" + Action: + - "ses:*EmailIdentity" + Resource: + - "*" + - Sid: "ManageUserPool" + Effect: "Allow" + Action: + - "cognito-idp:*ResourceServer" + - "cognito-idp:*UserPool" + - "cognito-idp:*UserPoolClient" + - "cognito-idp:*UserPoolDomain" + - "cognito-idp:List*" + - "cognito-idp:SetUserPoolMfaConfig" + - "cognito-idp:GetLogDeliveryConfiguration" + - "cognito-idp:SetLogDeliveryConfiguration" + Resource: + - "*" + - Sid: SNSPublish + Effect: "Allow" + Action: + - "sns:Publish" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:*" + + DeployPolicy6: + Type: AWS::IAM::ManagedPolicy + Properties: + # checkov:skip=CKV_AWS_111:test_create_star + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy6" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "ManageGlue" + Effect: "Allow" + Action: + - "glue:BatchGetPartition" + - "glue:BatchCreatePartition" + - "glue:BatchDeletePartition" + - "glue:BatchUpdatePartition" + - "glue:CreateCrawler" + - "glue:CreateDatabase" + - "glue:CreateTable" + - "glue:DeleteTable" + - "glue:DeleteCrawler" + - "glue:DeleteDatabase" + - "glue:GetDatabase" + - "glue:GetTable" + - "glue:UpdateCrawler" + - "glue:UpdateDatabase" + - "glue:UpdateTable" + Resource: + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:catalog" + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:crawler/*" + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:database/*" + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/*" + - !Sub "arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:userDefinedFunction/*" + - Sid: "GlueSecurityConfig" + Effect: "Allow" + Action: + - "glue:DeleteSecurityConfiguration" + - "glue:CreateSecurityConfiguration" + - "glue:GetSecurityConfiguration" + Resource: "*" + - Sid: "WafV2" + Effect: "Allow" + Action: + - "wafv2:*IPSet" + - "wafv2:*LoggingConfiguration" + - "wafv2:*PermissionPolicy" + - "wafv2:*RegexPatternSet" + - "wafv2:*RuleGroup" + - "wafv2:*WebACL" + - "wafv2:*FirewallManager*" + - "wafv2:GetSampledRequests" + - "wafv2:GetWebACLForResource" + - "wafv2:TagResource" + - "wafv2:UntagResource" + - "wafv2:ListTagsForResource" + - "wafv2:UpdateManagedRuleSetVersionExpiryDate" + Resource: + - !Sub "arn:${AWS::Partition}:wafv2:${AWS::Region}:127546899672:*" # this is an AWS account managing Shield Advanced rules + - !Sub "arn:${AWS::Partition}:wafv2:${AWS::Region}:${AWS::AccountId}:*" + - !Sub "arn:${AWS::Partition}:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:*" + - !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}::*" + - Sid: "WafV2Global" + Effect: "Allow" + Action: + - "wafv2:TagResource" + - "wafv2:UntagResource" + - "wafv2:GetWebACLForResource" + - "wafv2:GetWebACL" + Resource: + - !Sub "arn:${AWS::Partition}:wafv2:us-east-1:${AWS::AccountId}:*" + - Sid: "WafV2List" + Effect: "Allow" + Action: + - "wafv2:List*" + Resource: + - "*" + - Sid: "CloudFront" + Effect: "Allow" + Action: + - "cloudfront:CreateOriginRequestPolicy" + - "cloudfront:DeleteOriginRequestPolicy" + - "cloudfront:ListDistributionsByCachePolicyId" + - "cloudfront:GetCachePolicyConfig" + - "cloudfront:TagResource" + - "cloudfront:UpdateOriginRequestPolicy" + - "cloudfront:ListCachePolicies" + - "cloudfront:CreateDistribution" + - "cloudfront:ListOriginRequestPolicies" + - "cloudfront:GetOriginRequestPolicyConfig" + - "cloudfront:CreateCachePolicy" + - "cloudfront:GetDistribution" + - "cloudfront:UpdateCachePolicy" + - "cloudfront:ListTagsForResource" + - "cloudfront:GetOriginRequestPolicy" + - "cloudfront:ListDistributions" + - "cloudfront:DeleteCachePolicy" + - "cloudfront:UpdateDistribution" + - "cloudfront:GetCachePolicy" + - "cloudfront:DeleteDistribution" + - "cloudfront:UntagResource" + - "cloudfront:*Function*" + Resource: + - !Sub "arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:*" + - Sid: "CloudwatchSyntheticLambdaCreate" + Effect: "Allow" + Action: + - "lambda:CreateFunction" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:cwsyn-*" + - Sid: "CloudwatchSyntheticLambdaLayer" + Effect: "Allow" + Action: + - "lambda:GetLayerVersion" + Resource: + - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:*:layer:Synthetics:*" + - Sid: "CloudwatchSyntheticDelete" + Effect: "Allow" + Action: + - "synthetics:TagResource" + - "synthetics:*Canary" + - "synthetics:DescribeCanaries" + Resource: + - !Sub "arn:${AWS::Partition}:synthetics:${AWS::Region}:${AWS::AccountId}:canary:*" + - Sid: "CloudwatchSyntheticS3ObjectVersion" + Effect: "Allow" + Action: + - "s3:GetObjectVersion" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::aws-synthetics-library-${AWS::Region}/*" + - Sid: "UpdatedPipes" + Effect: "Allow" + Action: + - "pipes:*" + Resource: + - !Sub "arn:${AWS::Partition}:pipes:${AWS::Region}:${AWS::AccountId}:pipe/*" + - Sid: "ManageOIDCProvider" + Effect: "Allow" + Action: + - "iam:CreateOpenIDConnectProvider" + - "iam:DeleteOpenIDConnectProvider" + - "iam:GetOpenIDConnectProvider" + - "iam:TagOpenIDConnectProvider" + - "iam:UntagOpenIDConnectProvider" + - "iam:ListOpenIDConnectProviderTags" + - "iam:AddClientIDToOpenIDConnectProvider" + - "iam:RemoveClientIDFromOpenIDConnectProvider" + - "iam:UpdateOpenIDConnectProviderThumbprint" + Resource: + - !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:oidc-provider/*.account.gov.uk + - !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:oidc-provider/*.account.gov.uk/ + - Sid: "ListOIDCProvider" + Effect: "Allow" + Action: + - "iam:ListOpenIDConnectProviders" + Resource: + - "*" + - Sid: "RedshiftWriteConfig" + Effect: "Allow" + Action: + - "redshift-data:ExecuteStatement" + Resource: + - !Sub "arn:${AWS::Partition}:redshift:${AWS::Region}:${AWS::AccountId}:*" + - !Sub "arn:${AWS::Partition}:redshift-serverless:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "RedshiftReadConfig" + Effect: "Allow" + Action: + - "redshift-data:DescribeStatement" + - "redshift-data:GetStatementResult" + Resource: + - "*" + - Sid: "RDS" + Effect: "Allow" + Action: + - "rds:AddTagsToResource" + - "rds:CreateDBSecurityGroup" + - "rds:CreateDBInstance" + - "rds:CreateDBSubnetGroup" + - "rds:DeleteDBSubnetGroup" + - "rds:DescribeDBSubnetGroups" + - "rds:DescribeDBInstances" + - "rds:DeleteDBSecurityGroup" + - "rds:ListTagsForResource" + - "rds:ModifyDBInstance" + - "rds:ModifyDBSubnetGroup" + - "rds:RemoveTagsFromResource" + Resource: + - !Sub "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:secgrp:pact*" + - !Sub "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:pact*" + - !Sub "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:db:pact*" + - Sid: "ManageXRayResourcePolicy" + Effect: "Allow" + Action: + - "xray:DeleteResourcePolicy" + - "xray:ListResourcePolicies" + - "xray:PutResourcePolicy" + Resource: + - "*" + - Sid: "ManageScheduler" + Effect: "Allow" + Action: + - "scheduler:*" + Resource: + - !Sub "arn:${AWS::Partition}:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/*/*" + - Sid: "ClusterCapacityProviders" + Effect: "Allow" + Action: + - "ecs:PutClusterCapacityProviders" + Resource: + - !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:cluster/*" + + DeployPolicy7: + Type: AWS::IAM::ManagedPolicy + Properties: + # checkov:skip=CKV_AWS_111:test_create_star + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "DeployPolicy7" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "ManageServiceCatalog" + Effect: "Allow" + Action: + - "servicecatalog:CreatePortfolio" + - "servicecatalog:CreatePortfolioShare" + - "servicecatalog:CreateProvisionedProductPlan" + - "servicecatalog:DeleteProvisionedProductPlan" + - "servicecatalog:Describe*" + - "servicecatalog:ExecuteProvisionedProduct*" + - "servicecatalog:List*" + - "servicecatalog:ProvisionProduct" + - "servicecatalog:UpdatePortfolio" + - "servicecatalog:DeletePortfolio" + - "servicecatalog:RejectPortfolioShare" + - "servicecatalog:AcceptPortfolioShare" + - "servicecatalog:TagResource" + - "servicecatalog:UpdateProvisionedProduct" + Resource: + - !Sub "arn:${AWS::Partition}:catalog:${AWS::Region}:${AWS::AccountId}:portfolio/*" + - !Sub "arn:${AWS::Partition}:catalog:${AWS::Region}:${AWS::AccountId}:product/*" + - !Sub "arn:${AWS::Partition}:catalog:${AWS::Region}:${AWS::AccountId}:applications/*" + - !Sub "arn:${AWS::Partition}:catalog:${AWS::Region}:${AWS::AccountId}:attribute-groups/*" + + - Sid: "ManageServiceCatalogShare" + Effect: "Allow" + Action: + - "servicecatalog:CreateTagOption" + - "servicecatalog:DescribePortfolioShareStatus" + - "servicecatalog:DescribeProvisionedProduct" + - "servicecatalog:DescribeTagOption" + - "servicecatalog:DeleteTagOption" + - "servicecatalog:ListTagOptions" + - "servicecatalog:ListLaunchPaths" + - "servicecatalog:UpdateTagOption" + - "servicecatalog:UntagResource" + - "servicecatalog:TerminateProvisionedProduct" + Resource: + - "*" + - Sid: "ManageServiceCatalogProvisionedProducts" + Effect: "Allow" + Action: + - "cloudformation:DeleteStack" + - "cloudformation:DescribeStackEvents" + - "cloudformation:DescribeStacks" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/SC-${AWS::AccountId}-pp-*" + - Sid: "ClusterCapacityProviders" + Effect: "Allow" + Action: + - "ecs:PutClusterCapacityProviders" + Resource: + - !Sub "arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:cluster/*" + - Sid: "ManageAdminStackSet" + Effect: "Allow" + Action: + - "cloudformation:CreateStackInstances" + - "cloudformation:CreateStackSet" + - "cloudformation:DeleteStackInstances" + - "cloudformation:DeleteStackSet" + - "cloudformation:DescribeStackSet" + - "cloudformation:DescribeStackInstance" + - "cloudformation:DescribeStackSetOperation" + - "cloudformation:DetectStackSetDrift" + - "cloudformation:ListStackInstances" + - "cloudformation:ListStackSetOperationResults" + - "cloudformation:ListStackSetOperations" + - "cloudformation:StopStackSetOperation" + - "cloudformation:TagResource" + - "cloudformation:UntagResource" + - "cloudformation:UpdateStackInstances" + - "cloudformation:UpdateStackSet" + - "cloudformation:UntagResource" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:*:*:stackset*" + - !Sub "arn:${AWS::Partition}:cloudformation:*:*:type*" + - Sid: "ManageAdminStackSetGlobal" + Effect: "Allow" + Action: + - "cloudformation:GetTemplateSummary" + - "organizations:ListDelegatedAdministrators" + Resource: + - "*" + + DeployRole: + Type: AWS::IAM::Role + Properties: + # The role name is designed to ensure that it cannot match a role name + # that a template is allowed to create/modify as otherwise the template + # could modify the role that is used to execute it leading to a privilege + # escalation vulnerability. + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "DeployRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - !Ref CodeBuildPolicy + - !Ref DeployPolicy1 + - !Ref DeployPolicy2 + - !Ref DeployPolicy3 + - !Ref DeployPolicy4 + - !Ref DeployPolicy5 + - !Ref DeployPolicy6 + - !Ref DeployPolicy7 + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "DeployRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + DeployCodeBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "Deploy-${AWS::StackName}" + ServiceRole: !GetAtt DeployRole.Arn + Artifacts: + Type: "CODEPIPELINE" + Environment: + ComputeType: "BUILD_GENERAL1_SMALL" + Image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0" + Type: "LINUX_CONTAINER" + Source: + Type: "CODEPIPELINE" + # Buildspec syntax: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-syntax + BuildSpec: + Fn::Sub: + - | + version: 0.2 + env: + shell: bash + exported-variables: + - METRICS_ARGS + # Phase transitions: https://docs.aws.amazon.com/codebuild/latest/userguide/view-build-details.html#view-build-details-phases + phases: + install: + commands: + - wget --no-verbose https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip + - unzip -q aws-sam-cli-linux-x86_64.zip -d sam-installation + - ./sam-installation/install + - pip3 install "checkov==2.*" + + pre_build: + on-failure: ${AbortOnCheckovFailure} + commands: + - mkdir -p reports/checkov/ + - echo '\n' > reports/checkov/blank.xml + - checkov --framework cloudformation -f cf-template.yaml -o junitxml + --output-file-path=reports/checkov/; exit 0 + + build: + commands: + - | + echo "Deploying using devplatform sam-deploy-pipeline version: v2.52.4" + + - | + stack_state="$(aws cloudformation describe-stacks --stack-name "${SAMStackName}" \ + --query "Stacks[0].StackStatus" --output text || echo "NO_STACK")" + + - | + if [[ "$stack_state" = "ROLLBACK_COMPLETE" ]]; then \ + echo "Deleting stack ${SAMStackName} (in ROLLBACK_COMPLETE state) ..." \ + && aws cloudformation delete-stack --stack-name="${SAMStackName}" \ + && aws cloudformation wait stack-delete-complete --stack-name="${SAMStackName}"; \ + fi + + - | + inherited_tags="$(aws cloudformation describe-stacks \ + --stack-name ${AWS::StackName} \ + --query "Stacks[0].Tags[].[Key, Value]" \ + --output text | awk '{ split($0, pairs, "\t"); print pairs[1]"=\""pairs[2]"\"" }' | tr "\n" " ")" + + - | + provenance_tags="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[commitsha,repository]' --o text \ + | awk '{ split($0, tags, "\t"); print "commitsha=\"" tags[1] "\" repository=\"" tags[2] "\"" }')" + + - | + SKIP_CANARY_DEPLOYMENT="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[skipcanary]' --o text)" + + - | + commitsha="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[commitsha]' --o text)" + + - | + repository="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[repository]' --o text)" + + - | + mergetime="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[mergetime]' --o text \ + | tr " " ",")" + + - | + echo "Inherited Tags: $inherited_tags" + + - | + echo "Provenance Tags: $provenance_tags" + + - | + echo "Skip canary flag: $SKIP_CANARY_DEPLOYMENT" + + - | + chmod +x "$CODEBUILD_SRC_DIR_GrafanaScripts/grafana-annotations.sh" + + - | + chmod +x "$CODEBUILD_SRC_DIR_DynatraceScripts/dynatrace-metrics.sh" + + - | + shortsha=${!provenance_tags:11:7} + if [[ $shortsha = *[[:space:]]* ]]; then + shortsha=None + fi + + - | + echo "Short SHA: $shortsha" + + - | + ARGS="create \ + ${!shortsha} \ + ${Environment} \ + ${SAMStackName} \ + ${AWS::AccountId} \ + v2.52.4" + + - | + ANNOTATION_ID=$("$CODEBUILD_SRC_DIR_GrafanaScripts/grafana-annotations.sh" $ARGS) + + - | + METRICS_ARGS="create \ + --repository ${!repository} \ + --commit-sha ${!commitsha} \ + --merge-time ${!mergetime} \ + --environment ${Environment} \ + --sam-stack-name ${SAMStackName} \ + --account-id ${AWS::AccountId} \ + --stage "deploy" \ + --start-time-ms $CODEBUILD_START_TIME \ + --pipeline-version v2.52.4" + + - | + if [[ "${CodeSigningConfigArn}" == "none" ]]; then \ + PARAMETER_OVERRIDES="Environment=${Environment} VpcStackName=${VpcStackName} PermissionsBoundary=${AppPermissionsBoundaryArn} TestRoleArn=${TestRoleArn}"; else \ + PARAMETER_OVERRIDES="Environment=${Environment} VpcStackName=${VpcStackName} CodeSigningConfigArn=${CodeSigningConfigArn} PermissionsBoundary=${AppPermissionsBoundaryArn} TestRoleArn=${TestRoleArn}"; \ + fi + + - | + if [[ ($SKIP_CANARY_DEPLOYMENT -eq 0) && ("${ECSCanaryDeployment}" != "None") ]]; then \ + PARAMETER_OVERRIDES="$PARAMETER_OVERRIDES DeploymentStrategy=${ECSCanaryDeployment}"; \ + fi + + - | + if [[ ($SKIP_CANARY_DEPLOYMENT -eq 1) && ("${ECSCanaryDeployment}" != "None") ]]; then \ + PARAMETER_OVERRIDES="$PARAMETER_OVERRIDES DeploymentStrategy=CodeDeployDefault.ECSAllAtOnce"; \ + fi + + - | + if [[ ($SKIP_CANARY_DEPLOYMENT -eq 0) && ("${LambdaCanaryDeployment}" != "None") ]]; then \ + PARAMETER_OVERRIDES="$PARAMETER_OVERRIDES LambdaDeploymentPreference=${LambdaCanaryDeployment}"; \ + fi + + - | + if [[ ($SKIP_CANARY_DEPLOYMENT -eq 1) && ("${LambdaCanaryDeployment}" != "None") ]]; then \ + PARAMETER_OVERRIDES="$PARAMETER_OVERRIDES LambdaDeploymentPreference=AllAtOnce"; \ + fi + + - | + if AWS_RETRY_MODE=adaptive AWS_MAX_ATTEMPTS=8 sam deploy \ + --template-file cf-template.yaml \ + --s3-bucket "${PipelineBucket}" \ + --stack-name "${SAMStackName}" \ + --capabilities ${Capabilities} \ + --no-confirm-changeset \ + --no-fail-on-empty-changeset \ + --parameter-overrides $PARAMETER_OVERRIDES \ + --region ${AWS::Region} \ + --tags "$inherited_tags is-pipeline-deployment=True $provenance_tags"; then \ + "$CODEBUILD_SRC_DIR_GrafanaScripts/grafana-annotations.sh" update "completed" ${Environment} ${SAMStackName} $ANNOTATION_ID; else \ + "$CODEBUILD_SRC_DIR_GrafanaScripts/grafana-annotations.sh" update "rolled back" ${Environment} ${SAMStackName} $ANNOTATION_ID; exit 1; fi + + finally: + # This reads out the two dimensions from the pipeline, + # and publishes them to CloudWatch metrics as a release under the + # DevPlatform/SecurePipelines Namespace + - | + echo "build succeeded=$CODEBUILD_BUILD_SUCCEEDING" + - | + "$CODEBUILD_SRC_DIR_DynatraceScripts/dynatrace-metrics.sh" $METRICS_ARGS \ + --build-success $CODEBUILD_BUILD_SUCCEEDING + - | + COMMITSHA="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.commitsha' --o text)" + - | + aws cloudwatch put-metric-data --namespace DevPlatform/SecurePipelines/Releases \ + --metric-name CodeBuildSuccess \ + --unit None \ + --value $CODEBUILD_BUILD_SUCCEEDING \ + --dimensions stack_name=${SAMStackName},environment=${Environment},commitsha=$COMMITSHA + + reports: + checkov-reports: + files: + - "reports/checkov/*.xml" + file-format: "JUNITXML" + + - CodeSigningConfigArn: !If + - SigningProfile + - !Ref CodeSigningConfig + - "none" + AppPermissionsBoundaryArn: !If + - UseProgrammaticPermissionsBoundary + - !Ref AppProgrammaticPermissionsBoundary + - !Ref AppPermissionsBoundary + ArtifactBucket: !If + - CreateGitHubActionsResources + - !Ref GitHubArtifactSourceBucket + - !Select [5, !Split [':', !Ref ArtifactSourceBucketArn]] + TestRoleArn: !If + - CreateTestAction + - !GetAtt TestRole.Arn + - "none" + AbortOnCheckovFailure: !Ref AbortOnCheckovFailure + Capabilities: !If + - DeployECSCanaryStack + - CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND + - CAPABILITY_NAMED_IAM + ECSCanaryDeployment: !Ref ECSCanaryDeployment + LambdaCanaryDeployment: !Ref LambdaCanaryDeployment + SecondarySources: + - Type: S3 + Location: "template-bucket-templatebucket-35qbug5k1irh/sam-deploy-pipeline/grafana-scripts-v2.zip" + SourceIdentifier: GrafanaScripts + - Type: S3 + Location: "template-bucket-templatebucket-35qbug5k1irh/sam-deploy-pipeline/dynatrace-scripts.zip" + SourceIdentifier: DynatraceScripts + SecondarySourceVersions: + - SourceIdentifier: GrafanaScripts + SourceVersion: "iF__IZFI8spqJ8X.nW_6_JWHn.Wa0Cxf" + - SourceIdentifier: DynatraceScripts + SourceVersion: "TVSIGNYbCkHukYA0jtmABZNwITf_oTH4" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "DeployCodeBuildProject" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + DeployCodeBuildProjectLogGroup: + Type: AWS::Logs::LogGroup + Properties: + KmsKeyId: !GetAtt LogsKmsKey.Arn + LogGroupName: !Sub "/aws/codebuild/${DeployCodeBuildProject}" + RetentionInDays: !Ref LogRetentionDays + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "DeployCodeBuildProjectLogGroup" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Validate (CodeBuild) + # + + ValidateSignaturePolicy: + Type: AWS::IAM::ManagedPolicy + Condition: CreateTestContainerSignatureValidateStage + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "ValidateSignaturePolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "logs:DescribeLogGroups" + - "logs:DescribeLogStreams" + - "logs:DescribeMetricFilters" + - "logs:FilterLogEvents" + - "logs:GetLogEvents" + - "logs:StartQuery" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:*" + - Effect: "Allow" + Action: + - "logs:DescribeQueries" + - "logs:DescribeQueryDefinitions" + - "logs:GetLogDelivery" + - "logs:GetLogRecord" + - "logs:GetQueryResults" + - "logs:ListLogDeliveries" + - "logs:StopQuery" + - "logs:TestMetricFilter" + Resource: + - "*" + - Effect: "Allow" + Action: + - "ecr:BatchCheckLayerAvailability" + - "ecr:BatchGetImage" + - "ecr:GetDownloadUrlForLayer" + Resource: + - !Sub "arn:${AWS::Partition}:ecr:${AWS::Region}:*:repository/*" + - Effect: "Allow" + Action: + - "ecr:GetAuthorizationToken" + Resource: + - "*" + - Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Verify" + Resource: + - !Ref ContainerSignerKmsKeyArn + + ValidateSignatureRole: + Type: AWS::IAM::Role + Condition: CreateTestContainerSignatureValidateStage + Properties: + # The role name is designed to ensure that it cannot match a role name + # that a template is allowed to create/modify as otherwise the template + # could modify the role that is used to execute it leading to a privilege + # escalation vulnerability. + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "ValidateSignatureRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - !Ref CodeBuildPolicy + - !Ref ValidateSignaturePolicy + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TestRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + ValidateSignatureCodeBuildProject: + Type: AWS::CodeBuild::Project + Condition: CreateTestContainerSignatureValidateStage + Properties: + Name: !Sub "Validate-${AWS::StackName}" + ServiceRole: !GetAtt ValidateSignatureRole.Arn + Artifacts: + Type: "CODEPIPELINE" + Environment: + ComputeType: "BUILD_GENERAL1_SMALL" + Image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0" + Type: "LINUX_CONTAINER" + Source: + Type: "CODEPIPELINE" + BuildSpec: + Fn::Sub: + - | + version: 0.2 + env: + shell: bash + + phases: + install: + commands: + - wget --no-verbose "https://github.com/sigstore/cosign/releases/download/v1.9.0/cosign-linux-amd64" + - mv cosign-linux-amd64 /usr/local/bin/cosign + - chmod +x /usr/local/bin/cosign + + pre_build: + commands: + - | + registryUri=$(echo ${TestImage} | cut -d'/' -f1) + - | + aws ecr get-login-password --region ${AWS::Region} \ + | cosign login $registryUri \ + --username AWS \ + --password-stdin + + build: + commands: + - | + echo "Deploying using devplatform sam-deploy-pipeline version: v2.52.4" + + - | + cosign verify --key awskms:///${ContainerSignerKmsKeyArn} ${TestImage} + + cache: + paths: + - /usr/local/bin/* + + - ContainerSignerKmsKeyArn: !Ref ContainerSignerKmsKeyArn + TestImage: !Sub "${TestImageRepositoryUri}:latest" + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-ValidateSignatureCodeBuildProject" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + ValidateSignatureCodeBuildProjectLogGroup: + Type: AWS::Logs::LogGroup + Condition: CreateTestContainerSignatureValidateStage + Properties: + KmsKeyId: !GetAtt LogsKmsKey.Arn + LogGroupName: !Sub "/aws/codebuild/${ValidateSignatureCodeBuildProject}" + RetentionInDays: !Ref LogRetentionDays + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-ValidateSignatureCodeBuildProjectLogGroup" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Traffic Test (CodeBuild) + # + + TrafficTestPolicy: + Type: AWS::IAM::ManagedPolicy + Condition: CreateTrafficTest + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "TrafficTestPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "cloudformation:DescribeStacks" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${SAMStackName}/*" + - Effect: "Allow" + Action: + - "ssm:GetParameter*" + Resource: + - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/tests/${SAMStackName}/*" + - !If + - NotProduction + - Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + Resource: + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" + - !Ref AWS::NoValue + - !If + - NotProduction + - Effect: "Allow" + Action: + - "dynamodb:UpdateItem" + - "dynamodb:Query" + - "dynamodb:DeleteItem" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:*" + - !Ref AWS::NoValue + - !If + - CreateCustomKMSKeyPolicy + - Sid: "CustomKMSKeySigning" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + - "kms:GenerateDataKey" + Resource: !Ref "CustomKmsKeyArns" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "logs:DescribeLogGroups" + - "logs:DescribeLogStreams" + - "logs:DescribeMetricFilters" + - "logs:FilterLogEvents" + - "logs:GetLogEvents" + - "logs:StartQuery" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:*" + - Effect: "Allow" + Action: + - "logs:DescribeQueries" + - "logs:DescribeQueryDefinitions" + - "logs:GetLogDelivery" + - "logs:GetLogRecord" + - "logs:GetQueryResults" + - "logs:ListLogDeliveries" + - "logs:StopQuery" + - "logs:TestMetricFilter" + Resource: + - "*" + - Sid: "AccessSourceArtifacts" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + Resource: + - !If + - CreateGitHubActionsResources + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Ref ArtifactSourceBucketArn + - !If + - CreateGitHubActionsResources + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + - !Sub "${ArtifactSourceBucketArn}/*" + - Effect: "Allow" + Action: + - "sns:Publish" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:*" + - Effect: "Allow" + Action: + - "sqs:SendMessage" + - "sqs:DeleteMessage" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "TemplateBucketAccess" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetObjectTagging" + - "s3:ListBucket" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh" + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh/*" + - !Sub "arn:${AWS::Partition}:s3:::template-storage-templatebucket-1upzyw6v9cs42" + - !Sub "arn:${AWS::Partition}:s3:::template-storage-templatebucket-1upzyw6v9cs42/*" + - Sid: "ObservabilitySecretsManager" + Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: + - "arn:aws:secretsmanager:eu-west-2:216552277552:secret:*" # pragma: allowlist secret + - Sid: "ObservabilityKMS" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + Resource: + - "arn:aws:kms:eu-west-2:216552277552:key/*" + + TrafficTestRole: + Type: AWS::IAM::Role + Condition: CreateTrafficTest + Properties: + # The role name is designed to ensure that it cannot match a role name + # that a template is allowed to create/modify as otherwise the template + # could modify the role that is used to execute it leading to a privilege + # escalation vulnerability. + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "TrafficTestRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - !Ref CodeBuildPolicy + - !Ref TrafficTestPolicy + - !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TestRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + TrafficTestCodeBuildProject: + Type: AWS::CodeBuild::Project + Condition: CreateTrafficTest + Properties: + Name: !Sub "TrafficTest-${AWS::StackName}" + ServiceRole: !GetAtt TrafficTestRole.Arn + Artifacts: + Type: "NO_ARTIFACTS" + Environment: + ComputeType: !Ref TestComputeType + Image: !Sub "${TrafficTestImageRepositoryUri}:latest" + Type: "LINUX_CONTAINER" + ImagePullCredentialsType: "SERVICE_ROLE" + Source: + Type: "NO_SOURCE" + BuildSpec: !Sub | + version: 0.2 + + phases: + build: + commands: + - | + echo "Deploying using devplatform sam-deploy-pipeline version: v2.52.4" + - | + aws cloudformation describe-stacks --stack-name "${SAMStackName}" --region "${AWS::Region}" --query 'Stacks[0].Outputs[].{key: OutputKey, value: OutputValue}' --output text > cf-output.txt + - | + eval $(awk '{ printf("export CFN_%s=\"%s\"\n", $1, $2) }' cf-output.txt) + - export SAM_STACK_NAME="${SAMStackName}" + - /run-tests.sh + + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TrafficTestCodeBuildProject" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + TrafficTestCodeBuildProjectLogGroup: + Type: AWS::Logs::LogGroup + Condition: CreateTrafficTest + Properties: + KmsKeyId: !GetAtt LogsKmsKey.Arn + LogGroupName: !Sub "/aws/codebuild/${TrafficTestCodeBuildProject}" + RetentionInDays: !Ref LogRetentionDays + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TrafficTestCodeBuildProjectLogGroup" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Test (CodeBuild) + # + + TestPolicy: + Type: AWS::IAM::ManagedPolicy + Condition: CreateTestAction + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "TestPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "cloudformation:DescribeStacks" + Resource: + - !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${SAMStackName}/*" + - Effect: "Allow" + Action: + - "ssm:GetParameter*" + Resource: + - !Sub "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/tests/${SAMStackName}/*" + - !If + - NotProduction + - Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + Resource: + - !Sub "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*" + - !Ref AWS::NoValue + - !If + - NotProduction + - Effect: "Allow" + Action: + - "dynamodb:UpdateItem" + - "dynamodb:Query" + - "dynamodb:DeleteItem" + Resource: + - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:*" + - !Ref AWS::NoValue + - !If + - CreateCustomKMSKeyPolicy + - Sid: "CustomKMSKeySigning" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + - "kms:GenerateDataKey" + Resource: !Ref "CustomKmsKeyArns" + - !Ref AWS::NoValue + - Effect: "Allow" + Action: + - "logs:DescribeLogGroups" + - "logs:DescribeLogStreams" + - "logs:DescribeMetricFilters" + - "logs:FilterLogEvents" + - "logs:GetLogEvents" + - "logs:StartQuery" + Resource: + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:*" + - Effect: "Allow" + Action: + - "logs:DescribeQueries" + - "logs:DescribeQueryDefinitions" + - "logs:GetLogDelivery" + - "logs:GetLogRecord" + - "logs:GetQueryResults" + - "logs:ListLogDeliveries" + - "logs:StopQuery" + - "logs:TestMetricFilter" + Resource: + - "*" + - Sid: "AccessSourceArtifacts" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + Resource: + - !If + - CreateGitHubActionsResources + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Ref ArtifactSourceBucketArn + - !If + - CreateGitHubActionsResources + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + - !Sub "${ArtifactSourceBucketArn}/*" + - Effect: "Allow" + Action: + - "sns:Publish" + Resource: + - !Sub "arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:*" + - Effect: "Allow" + Action: + - "sqs:SendMessage" + - "sqs:PurgeQueue" + - "sqs:ReceiveMessage" + Resource: + - !Sub "arn:${AWS::Partition}:sqs:${AWS::Region}:${AWS::AccountId}:*" + - Sid: "TemplateBucketAccess" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetObjectTagging" + - "s3:ListBucket" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh" + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh/*" + - Sid: "ObservabilitySecretsManager" + Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: + - "arn:aws:secretsmanager:eu-west-2:216552277552:secret:*" # pragma: allowlist secret + - Sid: "ObservabilityKMS" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + Resource: + - "arn:aws:kms:eu-west-2:216552277552:key/*" + + VPCTestPolicy: + Type: AWS::IAM::ManagedPolicy + Condition: CreateVPCConfigurationForTestContainer + # checkov:skip=CKV_AWS_111:states:The mix of VPC permissions don't take a resource + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "VPCTestPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - ec2:CreateNetworkInterface + - ec2:DescribeDhcpOptions + - ec2:DescribeNetworkInterfaces + - ec2:DeleteNetworkInterface + - ec2:DescribeSubnets + - ec2:DescribeSecurityGroups + - ec2:DescribeVpcs + Resource: + - "*" + - Effect: Allow + Action: + - ec2:CreateNetworkInterfacePermission + Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*" + Condition: + StringEquals: + ec2:AuthorizedService: codebuild.amazonaws.com + ArnEquals: + ec2:Subnet: + - Fn::Sub: + - "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${SubnetId}" + - SubnetId: + Fn::ImportValue: + Fn::Sub: "${VpcStackName}-ProtectedSubnetIdA" + - Fn::Sub: + - "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${SubnetId}" + - SubnetId: + Fn::ImportValue: + Fn::Sub: "${VpcStackName}-ProtectedSubnetIdB" + + + TestRole: + Type: AWS::IAM::Role + Condition: CreateTestAction + Properties: + # The role name is designed to ensure that it cannot match a role name + # that a template is allowed to create/modify as otherwise the template + # could modify the role that is used to execute it leading to a privilege + # escalation vulnerability. + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "TestRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - !Ref CodeBuildPolicy + - !Ref TestPolicy + - !Sub "arn:${AWS::Partition}:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" + - !Ref LockToRegionPolicy + - !If + - CreateVPCConfigurationForTestContainer + - !Ref VPCTestPolicy + - !Ref AWS::NoValue + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TestRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + TestCodeBuildProject: + Type: AWS::CodeBuild::Project + Condition: CreateTestAction + Properties: + Name: !Sub "Test-${AWS::StackName}" + ServiceRole: !GetAtt TestRole.Arn + Artifacts: + Type: "CODEPIPELINE" + Environment: + ComputeType: !Ref TestComputeType + Image: !Sub "${TestImageRepositoryUri}:latest" + Type: "LINUX_CONTAINER" + ImagePullCredentialsType: "SERVICE_ROLE" + VpcConfig: + !If + - CreateVPCConfigurationForTestContainer + - SecurityGroupIds: + - !GetAtt TestContainerSecurityGroup.GroupId + Subnets: + - Fn::ImportValue: !Sub "${VpcStackName}-ProtectedSubnetIdA" + - Fn::ImportValue: !Sub "${VpcStackName}-ProtectedSubnetIdB" + VpcId: + Fn::ImportValue: !Sub "${VpcStackName}-VpcId" + - !Ref AWS::NoValue + Source: + Type: "CODEPIPELINE" + BuildSpec: + Fn::Sub: + - | + version: 0.2 + + env: + variables: + TEST_REPORT_DIR: results + COVERAGE_REPORT_DIR: coverage + exported-variables: + - METRICS_ARGS + + phases: + pre_build: + commands: + - mkdir -p "$TEST_REPORT_DIR" + - mkdir -p "$COVERAGE_REPORT_DIR" + - echo '' > coverage/clover.xml + + build: + commands: + - | + echo "Deploying using devplatform sam-deploy-pipeline version: v2.52.4" + + - | + commitsha="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[commitsha]' --o text)" + + - | + repository="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[repository]' --o text)" + + - | + mergetime="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[mergetime]' --o text \ + | tr " " ",")" + + - | + chmod +x "$CODEBUILD_SRC_DIR_DynatraceScripts/dynatrace-metrics.sh" + + - | + METRICS_ARGS="create \ + -r ${!repository} \ + -c ${!commitsha} \ + -m ${!mergetime} \ + -e ${Environment} \ + -s ${SAMStackName} \ + -a ${AWS::AccountId} \ + -l "test" \ + -t $CODEBUILD_START_TIME \ + -p v2.52.4" + + - | + aws cloudformation describe-stacks --stack-name "${SAMStackName}" --region "${AWS::Region}" --query 'Stacks[0].Outputs[].{key: OutputKey, value: OutputValue}' --output text > cf-output.txt + + - | + eval $(awk '{ printf("export CFN_%s=\"%s\"\n", $1, $2) }' cf-output.txt) + + - export TEST_REPORT_ABSOLUTE_DIR="$(pwd)/$TEST_REPORT_DIR" + - export COVERAGE_REPORT_ABSOLUTE_DIR="$(pwd)/$COVERAGE_REPORT_DIR" + - export TEST_ENVIRONMENT="${Environment}" + - export SAM_STACK_NAME="${SAMStackName}" + - /run-tests.sh + + finally: + - | + echo "build succeeded=$CODEBUILD_BUILD_SUCCEEDING" + - | + echo "METRICS_ARGS=$METRICS_ARGS" + - | + "$CODEBUILD_SRC_DIR_DynatraceScripts/dynatrace-metrics.sh" $METRICS_ARGS \ + -b $CODEBUILD_BUILD_SUCCEEDING || exit 0 + + reports: + TestReports: + files: + - results/**/* + file-format: ${TestReportFormat} + CoverageReports: + files: + - coverage/**/* + file-format: ${TestCoverageReportFormat} + - ArtifactBucket: !If + - CreateGitHubActionsResources + - !Ref GitHubArtifactSourceBucket + - !Select [5, !Split [':', !Ref ArtifactSourceBucketArn]] + SecondarySources: + - Type: S3 + Location: "template-bucket-templatebucket-35qbug5k1irh/sam-deploy-pipeline/dynatrace-scripts.zip" + SourceIdentifier: DynatraceScripts + SecondarySourceVersions: + - SourceIdentifier: DynatraceScripts + SourceVersion: "TVSIGNYbCkHukYA0jtmABZNwITf_oTH4" + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TestCodeBuildProject" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + TestCodeBuildProjectLogGroup: + Type: AWS::Logs::LogGroup + Condition: CreateTestAction + Properties: + KmsKeyId: !GetAtt LogsKmsKey.Arn + LogGroupName: !Sub "/aws/codebuild/${TestCodeBuildProject}" + RetentionInDays: !Ref LogRetentionDays + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-TestCodeBuildProjectLogGroup" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Promotion (CodeBuild) + # + + PromotionPolicy: + Condition: CreatePromoteStage + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "PromotionPolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Resource: + - !If + - CreateGitHubActionsResources + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Ref ArtifactSourceBucketArn + - !If + - CreateGitHubActionsResources + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + - !Sub "${ArtifactSourceBucketArn}/*" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetBucketVersioning" + - "s3:ListBucket" + - "s3:ListBucketVersions" + Effect: "Allow" + - Resource: + - !GetAtt ArtifactPromotionBucket.Arn + - !Sub "${ArtifactPromotionBucket.Arn}/*" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetObjectTagging" + - "s3:PutObject" + - "s3:PutObjectAcl" + - "s3:ListBucket" + Effect: "Allow" + - Sid: "TemplateBucketAccess" + Effect: "Allow" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetObjectTagging" + - "s3:ListBucket" + Resource: + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh" + - !Sub "arn:${AWS::Partition}:s3:::template-bucket-templatebucket-35qbug5k1irh/*" + - Sid: "ObservabilitySecretsManager" + Effect: "Allow" + Action: + - "secretsmanager:GetSecretValue" # pragma: allowlist secret + - "secretsmanager:ListSecrets" # pragma: allowlist secret + Resource: + - "arn:aws:secretsmanager:eu-west-2:216552277552:secret:*" # pragma: allowlist secret + - Sid: "ObservabilityKMS" + Effect: "Allow" + Action: + - "kms:DescribeKey" + - "kms:GetPublicKey" + - "kms:Decrypt" + - "kms:Verify" + Resource: + - "arn:aws:kms:eu-west-2:216552277552:key/*" + + PromotionRole: + Condition: CreatePromoteStage + Type: AWS::IAM::Role + Properties: + # The role name is designed to ensure that it cannot match a role name + # that a template is allowed to create/modify as otherwise the template + # could modify the role that is used to execute it leading to a privilege + # escalation vulnerability. + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "PromotionRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - !Ref CodeBuildPolicy + - !Ref PromotionPolicy + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "PromotionRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + PromotionCodeBuildProject: + Condition: CreatePromoteStage + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "Promotion-${AWS::StackName}" + ServiceRole: !GetAtt PromotionRole.Arn + Artifacts: + Type: "CODEPIPELINE" + Environment: + ComputeType: "BUILD_GENERAL1_SMALL" + Image: "aws/codebuild/amazonlinux2-x86_64-standard:3.0" + Type: "LINUX_CONTAINER" + Source: + Type: "CODEPIPELINE" + BuildSpec: !Sub + - | + version: 0.2 + + env: + variables: + PROMOTION_BUCKET_NAME: "${PromotionBucketName}" + SOURCE_ARTIFACT_BUCKET_NAME: "${ArtifactBucket}" + exported-variables: + - METRICS_ARGS + + phases: + install: + runtime-versions: + python: 3.x + + pre_build: + commands: + - pip install -r "$CODEBUILD_SRC_DIR_PromotionScript/requirements.txt" + + build: + commands: + - | + echo "Deploying using devplatform sam-deploy-pipeline version: v2.52.4" + + - | + commitsha="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[commitsha]' --o text)" + + - | + repository="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[repository]' --o text)" + + - | + mergetime="$(aws s3api head-object \ + --version-id $CODEBUILD_RESOLVED_SOURCE_VERSION \ + --bucket ${ArtifactBucket} \ + --key template.zip \ + --query='Metadata.[mergetime]' --o text \ + | tr " " ",")" + + - | + METRICS_ARGS="create \ + --repository ${!repository} \ + --commit-sha ${!commitsha} \ + --merge-time ${!mergetime} \ + --environment ${Environment} \ + --sam-stack-name ${SAMStackName} \ + --account-id ${AWS::AccountId} \ + --stage "promote" \ + --start-time-ms $CODEBUILD_START_TIME \ + --pipeline-version v2.52.4" + + - | + chmod +x "$CODEBUILD_SRC_DIR_DynatraceScripts/dynatrace-metrics.sh" + + - PYTHONPATH=$CODEBUILD_SRC_DIR_PromotionScript python -c "import promote; promote.main('$PROMOTION_BUCKET_NAME', '$SOURCE_ARTIFACT_BUCKET_NAME', '$CODEBUILD_RESOLVED_SOURCE_VERSION')" + + finally: + - | + echo "build succeeded=$CODEBUILD_BUILD_SUCCEEDING" + - | + "$CODEBUILD_SRC_DIR_DynatraceScripts/dynatrace-metrics.sh" $METRICS_ARGS \ + --build-success $CODEBUILD_BUILD_SUCCEEDING + + - PromotionBucketName: !Select [5, !Split [':', !GetAtt ArtifactPromotionBucket.Arn]] + ArtifactBucket: !If + - CreateGitHubActionsResources + - !Ref GitHubArtifactSourceBucket + - !Select [ 5, !Split [ ':', !Ref ArtifactSourceBucketArn ] ] + SecondarySources: + - Type: S3 + Location: "template-bucket-templatebucket-35qbug5k1irh/sam-deploy-pipeline/dynatrace-scripts.zip" + SourceIdentifier: DynatraceScripts + - Type: S3 + Location: "template-bucket-templatebucket-35qbug5k1irh/sam-deploy-pipeline/promotion_sources.zip" + SourceIdentifier: PromotionScript + SecondarySourceVersions: + - SourceIdentifier: DynatraceScripts + SourceVersion: "TVSIGNYbCkHukYA0jtmABZNwITf_oTH4" + - SourceIdentifier: PromotionScript + SourceVersion: "dyg4lZ6HyUp1jMyRxMZqw4fStm_1DlSE" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "PromotionCodeBuildProject" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + PromotionCodeBuildProjectLogGroup: + Type: AWS::Logs::LogGroup + Condition: CreatePromoteStage + Properties: + KmsKeyId: !GetAtt LogsKmsKey.Arn + LogGroupName: !Sub "/aws/codebuild/${PromotionCodeBuildProject}" + RetentionInDays: !Ref LogRetentionDays + Tags: + - Key: "Name" + Value: !Sub "${AWS::StackName}-PromotionCodeBuildProjectLogGroup" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Pipeline + # + + PipelineBucket: + Type: AWS::S3::Bucket + Properties: + LifecycleConfiguration: + Rules: + - Id: artifactBucketExpiry + Status: Enabled + ExpirationInDays: 180 + VersioningConfiguration: + Status: "Enabled" + PublicAccessBlockConfiguration: + BlockPublicAcls: true + BlockPublicPolicy: true + IgnorePublicAcls: true + RestrictPublicBuckets: true + LoggingConfiguration: + DestinationBucketName: !Ref S3AccessLogsBucket + LogFilePrefix: "pipeline/" + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: "AES256" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "pipelinebucket" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + PipelineBucketPolicy: + Type: AWS::S3::BucketPolicy + Properties: + Bucket: !Ref PipelineBucket + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Deny" + Resource: + - !GetAtt PipelineBucket.Arn + - !Sub "${PipelineBucket.Arn}/*" + Principal: "*" + Action: "*" + Condition: + Bool: + "aws:SecureTransport": false + + CodePipelinePolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + ManagedPolicyName: + Fn::Join: + - "-" + - - !Ref AWS::StackName + - "CodePipelinePolicy" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + PolicyDocument: + Version: "2012-10-17" + Statement: + - Resource: + - !If + - CreateGitHubActionsResources + - !GetAtt GitHubArtifactSourceBucket.Arn + - !Ref ArtifactSourceBucketArn + - !If + - CreateGitHubActionsResources + - !Sub "${GitHubArtifactSourceBucket.Arn}/*" + - !Sub "${ArtifactSourceBucketArn}/*" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetBucketVersioning" + Effect: "Allow" + - Resource: + - !GetAtt PipelineBucket.Arn + - !Sub "${PipelineBucket.Arn}/*" + Action: + - "s3:GetObject" + - "s3:GetObjectVersion" + - "s3:GetBucketVersioning" + - "s3:PutObjectAcl" + - "s3:PutObject" + Effect: "Allow" + - Resource: + - !GetAtt DeployCodeBuildProject.Arn + - !If [CreateTestContainerSignatureValidateStage, !GetAtt ValidateSignatureCodeBuildProject.Arn, !Ref "AWS::NoValue"] + - !If [CreateTestAction, !GetAtt TestCodeBuildProject.Arn, !Ref "AWS::NoValue"] + - !If [CreateTrafficTest, !GetAtt TrafficTestCodeBuildProject.Arn, !Ref "AWS::NoValue"] + - !If [CreatePromoteStage, !GetAtt PromotionCodeBuildProject.Arn, !Ref "AWS::NoValue"] + Action: + - "codebuild:BatchGetBuilds" + - "codebuild:StartBuild" + - "codebuild:BatchGetBuildBatches" + - "codebuild:StartBuildBatch" + Effect: "Allow" + + CodePipelineRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "codepipeline.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - !Ref CodePipelinePolicy + - !Ref LockToRegionPolicy + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "CodePipelineRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + Pipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + RoleArn: !GetAtt CodePipelineRole.Arn + ArtifactStore: + Type: "S3" + Location: !Ref PipelineBucket + Name: !If + - PipelineEnvironmentNameEnabled + - !Join + - "-" + - - !Ref AWS::StackName + - !Ref Environment + - "Pipeline" + - Fn::Select: + - 4 + - Fn::Split: + - '-' + - Fn::Select: + - 2 + - Fn::Split: + - / + - Ref: AWS::StackId + - !Ref AWS::NoValue + Stages: + - Name: "Fetch" + Actions: + - Name: "Get-Artifacts" + ActionTypeId: + Category: "Source" + Version: "1" + Provider: "S3" + Owner: "AWS" + Configuration: + S3Bucket: !If + - CreateGitHubActionsResources + - !Ref GitHubArtifactSourceBucket + - !Select [5, !Split [':', !Ref ArtifactSourceBucketArn]] + S3ObjectKey: "template.zip" + PollForSourceChanges: !If [EnablePipelinePolling, "true", "false"] + OutputArtifacts: + - Name: "sam-artifacts" + - !If + - CreateManualApprovalStage + - Name: "Approval" + Actions: + - Name: "Deploy-Approval" + ActionTypeId: + Category: "Approval" + Version: "1" + Provider: "Manual" + Owner: "AWS" + Configuration: + CustomData: "Click approve to proceed to Deploy stage" + - !Ref AWS::NoValue + - Name: "Deploy" + Actions: + - Name: "Deploy" + ActionTypeId: + Category: "Build" + Version: "1" + Provider: "CodeBuild" + Owner: "AWS" + Configuration: + ProjectName: !Ref DeployCodeBuildProject + InputArtifacts: + - Name: "sam-artifacts" + RunOrder: 1 + - !If + - CreateTestContainerSignatureValidateStage + - Name: ValidateTestSignature + ActionTypeId: + Category: "Build" + Version: "1" + Provider: "CodeBuild" + Owner: "AWS" + Configuration: + ProjectName: !Ref ValidateSignatureCodeBuildProject + InputArtifacts: + - Name: "sam-artifacts" + RunOrder: 2 + - !Ref AWS::NoValue + - !If + - CreateTestAction + - Name: Test + ActionTypeId: + Category: "Build" + Version: "1" + Provider: "CodeBuild" + Owner: "AWS" + Configuration: + ProjectName: !Ref TestCodeBuildProject + InputArtifacts: + - Name: "sam-artifacts" + RunOrder: 3 + - !Ref AWS::NoValue + - !If + - CreatePromoteStage + - Name: "Promote" + Actions: + - Name: "Promote" + ActionTypeId: + Category: "Build" + Version: "1" + Provider: "CodeBuild" + Owner: "AWS" + Configuration: + ProjectName: !Ref PromotionCodeBuildProject + InputArtifacts: + - Name: "sam-artifacts" + - !Ref AWS::NoValue + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "Pipeline" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + # + # Event driven triggers + # + + DeploymentTriggerCloudWatchEventRole: + Type: AWS::IAM::Role + Properties: + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "DepTrigRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - events.amazonaws.com + Action: sts:AssumeRole + Path: / + Policies: + - PolicyName: cwe-pipeline-execution + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: codepipeline:StartPipelineExecution + Resource: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "DeploymentTriggerCloudWatchEventRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + DeploymentTriggerCloudWatchEventRule: + Type: AWS::Events::Rule + Properties: + EventPattern: + source: + - aws.s3 + detail-type: + - 'AWS API Call via CloudTrail' + detail: + eventSource: + - s3.amazonaws.com + eventName: + - CopyObject + - PutObject + - CompleteMultipartUpload + requestParameters: + bucketName: + - !If + - CreateGitHubActionsResources + - !Ref GitHubArtifactSourceBucket + - !Select [5, !Split [':', !Ref ArtifactSourceBucketArn]] + key: + - "template.zip" + Targets: + - Arn: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}" + RoleArn: !GetAtt DeploymentTriggerCloudWatchEventRole.Arn + Id: codepipeline-Pipeline + + DeploymentTriggerEventBusPolicy: + Condition: CreateDeploymentTriggerEventBusPolicy + Type: AWS::Events::EventBusPolicy + Properties: + StatementId: !Sub "${AWS::StackName}-DepTrigEventBusPolicy" + Statement: + Effect: "Allow" + Principal: + AWS: !Ref ArtifactSourceBucketEventTriggerRoleArn + Action: "events:PutEvents" + Resource: !Sub "arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:event-bus/default" + + TrafficTestTriggerCloudWatchEventRole: + Type: AWS::IAM::Role + Condition: CreateTrafficTest + Properties: + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "TrfTrigRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - events.amazonaws.com + Action: sts:AssumeRole + Path: / + Policies: + - PolicyName: cwe-codebuild-execution + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - codebuild:StartBuild + - codebuild:StopBuild + + Resource: !Sub "arn:${AWS::Partition}:codebuild:${AWS::Region}:${AWS::AccountId}:project/${TrafficTestCodeBuildProject}" + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "TrafficTestTriggerCloudWatchEventRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + TrafficTestTriggerCloudWatchEventRule: + Condition: CreateTrafficTest + Type: AWS::Events::Rule + Properties: + EventPattern: + source: + - aws.codebuild + detail-type: + - CodeBuild Build State Change + detail: + project-name: + - !Ref DeployCodeBuildProject + build-status: + - "IN_PROGRESS" + State: ENABLED + Targets: + - Id: codepipeline-Pipeline + Arn: !GetAtt TrafficTestCodeBuildProject.Arn + RoleArn: !GetAtt TrafficTestTriggerCloudWatchEventRole.Arn + + PromotionTriggerCloudWatchEventRole: + Condition: CreatePromoteStage + Type: AWS::IAM::Role + Properties: + RoleName: + Fn::Join: + - "-" + - - "PL" + - !If + - UseTruncatedPipelineStackName + - !Ref TruncatedPipelineStackName + - !Ref AWS::StackName + - "PromoTrigRole" + - Fn::Select: + - 4 + - Fn::Split: + - "-" + - Fn::Select: + - 2 + - Fn::Split: + - "/" + - Ref: AWS::StackId + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - events.amazonaws.com + Action: sts:AssumeRole + Path: / + Policies: + - PolicyName: event-bus-putevents + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: events:PutEvents + Resource: + !Sub + - "arn:${AWS::Partition}:events:${AWS::Region}:${AllowedAccount}:event-bus/default" + - AllowedAccount: !Select [0, !Split [',', !Ref AllowedAccounts]] + - !If + - UseTwoAllowedAccounts + - Effect: Allow + Action: events:PutEvents + Resource: + !Sub + - "arn:${AWS::Partition}:events:${AWS::Region}:${AllowedAccount}:event-bus/default" + - AllowedAccount: !Select [1, !Split [',', !Ref AllowedAccounts]] + - !Ref AWS::NoValue + Tags: + - Key: "Name" + Value: !Join + - "-" + - - !Ref AWS::StackName + - "PromotionTriggerCloudWatchEventRole" + - Key: "Service" + Value: "ci/cd" + - Key: "Source" + Value: "govuk-one-login/devplatform-deploy/sam-deploy-pipeline/template.yaml" + + PromotionTriggerCloudWatchEventRule: + Condition: CreatePromoteStage + Type: AWS::Events::Rule + Properties: + EventPattern: + source: + - aws.s3 + detail-type: + - 'AWS API Call via CloudTrail' + detail: + eventSource: + - s3.amazonaws.com + eventName: + - CopyObject + - PutObject + - CompleteMultipartUpload + requestParameters: + bucketName: + - !Ref ArtifactPromotionBucket + key: + - "template.zip" + Targets: + - Arn: + !Sub + - "arn:${AWS::Partition}:events:${AWS::Region}:${AllowedAccount}:event-bus/default" + - AllowedAccount: !Select [0, !Split [',', !Ref AllowedAccounts]] + RoleArn: !GetAtt PromotionTriggerCloudWatchEventRole.Arn + Id: Upstream-event-bus1 + - !If + - UseTwoAllowedAccounts + - Arn: + !Sub + - "arn:${AWS::Partition}:events:${AWS::Region}:${AllowedAccount}:event-bus/default" + - AllowedAccount: !Select [1, !Split [',', !Ref AllowedAccounts]] + RoleArn: !GetAtt PromotionTriggerCloudWatchEventRole.Arn + Id: Upstream-event-bus2 + - !Ref AWS::NoValue + + TestContainerSecurityGroup: + Type: "AWS::EC2::SecurityGroup" + Condition: CreateVPCConfigurationForTestContainer + Properties: + GroupDescription: >- + Permits unrestricted outbound on 443 to allow the testcontainer to access the internet over SSL. + SecurityGroupEgress: + - CidrIp: 0.0.0.0/0 + Description: Allow to the wider internet on port 443 + FromPort: 443 + IpProtocol: tcp + ToPort: 443 + - CidrIp: 0.0.0.0/0 + Description: Allow 8089 for splunk api access + FromPort: 8089 + IpProtocol: tcp + ToPort: 8089 + VpcId: + Fn::ImportValue: !Sub "${VpcStackName}-VpcId" + + # + # Slack build notification rule + # + + NotificationRule: + Type: AWS::CodeStarNotifications::NotificationRule + Condition: EmitBuildNotifications + Properties: + Name: !Sub + - "${StackName}-build-notifications" + - StackName: !If [UseTruncatedPipelineStackName, !Ref TruncatedPipelineStackName, !Ref AWS::StackName] + Resource: !Sub + - "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}" + - Pipeline: !Ref Pipeline + DetailType: "BASIC" + EventTypeIds: + !FindInMap [ BuildNotifications, !Ref SlackNotificationType, "EventTypeIds" ] + Targets: + - TargetType: "SNS" + TargetAddress: + Fn::ImportValue: + !Sub "${BuildNotificationStackName}-BuildNotificationTopicArn"