Skip to content

Instantly share code, notes, and snippets.

@cagingulsen
Created May 9, 2022 20:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cagingulsen/e180ed4b10cc5baf3b636d6c42c1cb34 to your computer and use it in GitHub Desktop.
Save cagingulsen/e180ed4b10cc5baf3b636d6c42c1cb34 to your computer and use it in GitHub Desktop.
AWS CDK Bootstrap Template for Custom Bootstrapping - Version 12 - with a Permissions Boundary
Description: This stack includes resources needed to deploy AWS CDK apps into this
environment
Parameters:
TrustedAccounts:
Description: List of AWS accounts that are trusted to publish assets and deploy
stacks to this environment
Default: ''
Type: CommaDelimitedList
TrustedAccountsForLookup:
Description: List of AWS accounts that are trusted to look up values in this
environment
Default: ''
Type: CommaDelimitedList
CloudFormationExecutionPolicies:
Description: List of the ManagedPolicy ARN(s) to attach to the CloudFormation
deployment role
Default: ''
Type: CommaDelimitedList
FileAssetsBucketName:
Description: The name of the S3 bucket used for file assets
Default: ''
Type: String
FileAssetsBucketKmsKeyId:
Description: Empty to create a new key (default), 'AWS_MANAGED_KEY' to use a managed
S3 key, or the ID/ARN of an existing key.
Default: ''
Type: String
ContainerAssetsRepositoryName:
Description: A user-provided custom name to use for the container assets ECR repository
Default: ''
Type: String
Qualifier:
Description: An identifier to distinguish multiple bootstrap stacks in the same environment
Default: hnb659fds
Type: String
# "cdk-(qualifier)-image-publishing-role-(account)-(region)" needs to be <= 64 chars
# account = 12, region <= 14, 10 chars for qualifier and 28 for rest of role name
AllowedPattern: "[A-Za-z0-9_-]{1,10}"
ConstraintDescription: Qualifier must be an alphanumeric identifier of at most 10 characters
PublicAccessBlockConfiguration:
Description: Whether or not to enable S3 Staging Bucket Public Access Block Configuration
Default: 'true'
Type: 'String'
AllowedValues: ['true', 'false']
PermissionsBoundaryArn:
Description: ARN of the Permissions Boundary
Type: 'String'
Conditions:
HasTrustedAccounts:
Fn::Not:
- Fn::Equals:
- ''
- Fn::Join:
- ''
- Ref: TrustedAccounts
HasTrustedAccountsForLookup:
Fn::Not:
- Fn::Equals:
- ''
- Fn::Join:
- ''
- Ref: TrustedAccountsForLookup
HasCloudFormationExecutionPolicies:
Fn::Not:
- Fn::Equals:
- ''
- Fn::Join:
- ''
- Ref: CloudFormationExecutionPolicies
HasCustomFileAssetsBucketName:
Fn::Not:
- Fn::Equals:
- ''
- Ref: FileAssetsBucketName
CreateNewKey:
Fn::Equals:
- ''
- Ref: FileAssetsBucketKmsKeyId
UseAwsManagedKey:
Fn::Equals:
- 'AWS_MANAGED_KEY'
- Ref: FileAssetsBucketKmsKeyId
HasCustomContainerAssetsRepositoryName:
Fn::Not:
- Fn::Equals:
- ''
- Ref: ContainerAssetsRepositoryName
UsePublicAccessBlockConfiguration:
Fn::Equals:
- 'true'
- Ref: PublicAccessBlockConfiguration
Resources:
FileAssetsBucketEncryptionKey:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Statement:
- Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
- kms:GenerateDataKey
Effect: Allow
Principal:
AWS:
Ref: AWS::AccountId
Resource: "*"
- Action:
- kms:Decrypt
- kms:DescribeKey
- kms:Encrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
Effect: Allow
Principal:
# Not actually everyone -- see below for Conditions
AWS: "*"
Resource: "*"
Condition:
StringEquals:
# See https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-caller-account
kms:CallerAccount:
Ref: AWS::AccountId
kms:ViaService:
- Fn::Sub: s3.${AWS::Region}.amazonaws.com
- Action:
- kms:Decrypt
- kms:DescribeKey
- kms:Encrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
Effect: Allow
Principal:
AWS:
Fn::Sub: "${FilePublishingRole.Arn}"
Resource: "*"
Condition: CreateNewKey
FileAssetsBucketEncryptionKeyAlias:
Condition: CreateNewKey
Type: AWS::KMS::Alias
Properties:
AliasName:
Fn::Sub: "alias/cdk-${Qualifier}-assets-key"
TargetKeyId:
Ref: FileAssetsBucketEncryptionKey
StagingBucket:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::If:
- HasCustomFileAssetsBucketName
- Fn::Sub: "${FileAssetsBucketName}"
- Fn::Sub: cdk-${Qualifier}-assets-${AWS::AccountId}-${AWS::Region}
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID:
Fn::If:
- CreateNewKey
- Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}"
- Fn::If:
- UseAwsManagedKey
- Ref: AWS::NoValue
- Fn::Sub: "${FileAssetsBucketKmsKeyId}"
PublicAccessBlockConfiguration:
Fn::If:
- UsePublicAccessBlockConfiguration
- BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
- Ref: AWS::NoValue
VersioningConfiguration:
Status: Enabled
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
StagingBucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: { Ref: 'StagingBucket' }
PolicyDocument:
Id: 'AccessControl'
Version: '2012-10-17'
Statement:
- Sid: 'AllowSSLRequestsOnly'
Action: 's3:*'
Effect: 'Deny'
Resource:
- { 'Fn::Sub': '${StagingBucket.Arn}' }
- { 'Fn::Sub': '${StagingBucket.Arn}/*' }
Condition:
Bool: { 'aws:SecureTransport': 'false' }
Principal: '*'
ContainerAssetsRepository:
Type: AWS::ECR::Repository
Properties:
ImageScanningConfiguration:
ScanOnPush: true
RepositoryName:
Fn::If:
- HasCustomContainerAssetsRepositoryName
- Fn::Sub: "${ContainerAssetsRepositoryName}"
- Fn::Sub: cdk-${Qualifier}-container-assets-${AWS::AccountId}-${AWS::Region}
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
# Necessary for Lambda container images
# https://docs.aws.amazon.com/lambda/latest/dg/configuration-images.html#configuration-images-permissions
- Sid: LambdaECRImageRetrievalPolicy
Effect: Allow
Principal: { Service: "lambda.amazonaws.com" }
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Condition:
StringLike:
"aws:sourceArn": { "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" }
FilePublishingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: AWS::AccountId
- Fn::If:
- HasTrustedAccounts
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: TrustedAccounts
- Ref: AWS::NoValue
RoleName:
Fn::Sub: cdk-${Qualifier}-file-publishing-role-${AWS::AccountId}-${AWS::Region}
PermissionsBoundary: !Ref PermissionsBoundaryArn
Tags:
- Key: aws-cdk:bootstrap-role
Value: file-publishing
ImagePublishingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: AWS::AccountId
- Fn::If:
- HasTrustedAccounts
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: TrustedAccounts
- Ref: AWS::NoValue
RoleName:
Fn::Sub: cdk-${Qualifier}-image-publishing-role-${AWS::AccountId}-${AWS::Region}
PermissionsBoundary: !Ref PermissionsBoundaryArn
Tags:
- Key: aws-cdk:bootstrap-role
Value: image-publishing
LookupRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: AWS::AccountId
- Fn::If:
- HasTrustedAccountsForLookup
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: TrustedAccountsForLookup
- Ref: AWS::NoValue
- Fn::If:
- HasTrustedAccounts
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: TrustedAccounts
- Ref: AWS::NoValue
RoleName:
Fn::Sub: cdk-${Qualifier}-lookup-role-${AWS::AccountId}-${AWS::Region}
PermissionsBoundary: !Ref PermissionsBoundaryArn
ManagedPolicyArns:
- Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess"
Policies:
- PolicyDocument:
Statement:
- Sid: DontReadSecrets
Effect: Deny
Action:
- kms:Decrypt
Resource: "*"
Version: '2012-10-17'
PolicyName: LookupRolePolicy
Tags:
- Key: aws-cdk:bootstrap-role
Value: lookup
FilePublishingRoleDefaultPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Statement:
- Action:
- s3:GetObject*
- s3:GetBucket*
- s3:GetEncryptionConfiguration
- s3:List*
- s3:DeleteObject*
- s3:PutObject*
- s3:Abort*
Resource:
- Fn::Sub: "${StagingBucket.Arn}"
- Fn::Sub: "${StagingBucket.Arn}/*"
Effect: Allow
- Action:
- kms:Decrypt
- kms:DescribeKey
- kms:Encrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
Effect: Allow
Resource:
Fn::If:
- CreateNewKey
- Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}"
- Fn::Sub: arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/${FileAssetsBucketKmsKeyId}
Version: '2012-10-17'
Roles:
- Ref: FilePublishingRole
PolicyName:
Fn::Sub: cdk-${Qualifier}-file-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region}
ImagePublishingRoleDefaultPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Statement:
- Action:
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
- ecr:BatchCheckLayerAvailability
- ecr:DescribeRepositories
- ecr:DescribeImages
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Resource:
Fn::Sub: "${ContainerAssetsRepository.Arn}"
Effect: Allow
- Action:
- ecr:GetAuthorizationToken
Resource: "*"
Effect: Allow
Version: '2012-10-17'
Roles:
- Ref: ImagePublishingRole
PolicyName:
Fn::Sub: cdk-${Qualifier}-image-publishing-role-default-policy-${AWS::AccountId}-${AWS::Region}
DeploymentActionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: AWS::AccountId
- Fn::If:
- HasTrustedAccounts
- Action: sts:AssumeRole
Effect: Allow
Principal:
AWS:
Ref: TrustedAccounts
- Ref: AWS::NoValue
Policies:
- PolicyDocument:
Statement:
- Sid: CloudFormationPermissions
Effect: Allow
Action:
- cloudformation:CreateChangeSet
- cloudformation:DeleteChangeSet
- cloudformation:DescribeChangeSet
- cloudformation:DescribeStacks
- cloudformation:ExecuteChangeSet
- cloudformation:CreateStack
- cloudformation:UpdateStack
Resource: "*"
- Sid: PipelineCrossAccountArtifactsBucket
# Read/write buckets in different accounts. Permissions to buckets in
# same account are granted by bucket policies.
#
# Write permissions necessary to write outputs to the cross-region artifact replication bucket
# https://aws.amazon.com/premiumsupport/knowledge-center/codepipeline-deploy-cloudformation/.
Effect: Allow
Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
- s3:Abort*
- s3:DeleteObject*
- s3:PutObject*
Resource: "*"
Condition:
StringNotEquals:
s3:ResourceAccount:
Ref: 'AWS::AccountId'
- Sid: PipelineCrossAccountArtifactsKey
# Use keys only for the purposes of reading encrypted files from S3.
Effect: Allow
Action:
- kms:Decrypt
- kms:DescribeKey
- kms:Encrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
Resource: "*"
Condition:
StringEquals:
kms:ViaService:
Fn::Sub: s3.${AWS::Region}.amazonaws.com
- Action: iam:PassRole
Resource:
Fn::Sub: "${CloudFormationExecutionRole.Arn}"
Effect: Allow
- Sid: CliPermissions
Action:
# Permissions needed by the CLI when doing `cdk deploy`.
# Our CI/CD does not need DeleteStack,
# but we also want to use this role from the CLI,
# and there you can call `cdk destroy`
- cloudformation:DescribeStackEvents
- cloudformation:GetTemplate
- cloudformation:DeleteStack
- cloudformation:UpdateTerminationProtection
- sts:GetCallerIdentity
# `cdk import`
- cloudformation:GetTemplateSummary
Resource: "*"
Effect: Allow
- Sid: CliStagingBucket
Effect: Allow
Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
Resource:
- Fn::Sub: ${StagingBucket.Arn}
- Fn::Sub: ${StagingBucket.Arn}/*
- Sid: ReadVersion
Effect: Allow
Action:
- ssm:GetParameter
Resource:
- Fn::Sub: "arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter${CdkBootstrapVersion}"
Version: '2012-10-17'
PolicyName: default
RoleName:
Fn::Sub: cdk-${Qualifier}-deploy-role-${AWS::AccountId}-${AWS::Region}
PermissionsBoundary: !Ref PermissionsBoundaryArn
Tags:
- Key: aws-cdk:bootstrap-role
Value: deploy
CloudFormationExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: cloudformation.amazonaws.com
Version: '2012-10-17'
ManagedPolicyArns:
Fn::If:
- HasCloudFormationExecutionPolicies
- Ref: CloudFormationExecutionPolicies
- Fn::If:
- HasTrustedAccounts
# The CLI will prevent this case from occurring
- Ref: AWS::NoValue
# The CLI will advertise that we picked this implicitly
- - Fn::Sub: "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
RoleName:
Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region}
PermissionsBoundary: !Ref PermissionsBoundaryArn
# The SSM parameter is used in pipeline-deployed templates to verify the version
# of the bootstrap resources.
CdkBootstrapVersion:
Type: AWS::SSM::Parameter
Properties:
Type: String
Name:
Fn::Sub: '/cdk-bootstrap/${Qualifier}/version'
Value: '12'
Outputs:
BucketName:
Description: The name of the S3 bucket owned by the CDK toolkit stack
Value:
Fn::Sub: "${StagingBucket}"
BucketDomainName:
Description: The domain name of the S3 bucket owned by the CDK toolkit stack
Value:
Fn::Sub: "${StagingBucket.RegionalDomainName}"
# @deprecated - This Export can be removed at some future point in time.
# We can't do it today because if there are stacks that use it, the bootstrap
# stack cannot be updated. Not used anymore by apps >= 1.60.0
FileAssetKeyArn:
Description: The ARN of the KMS key used to encrypt the asset bucket (deprecated)
Value:
Fn::If:
- CreateNewKey
- Fn::Sub: "${FileAssetsBucketEncryptionKey.Arn}"
- Fn::Sub: "${FileAssetsBucketKmsKeyId}"
Export:
Name:
Fn::Sub: CdkBootstrap-${Qualifier}-FileAssetKeyArn
ImageRepositoryName:
Description: The name of the ECR repository which hosts docker image assets
Value:
Fn::Sub: "${ContainerAssetsRepository}"
# The Output is used by the CLI to verify the version of the bootstrap resources.
BootstrapVersion:
Description: The version of the bootstrap resources that are currently mastered
in this stack
Value:
Fn::GetAtt: [CdkBootstrapVersion, Value]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment