in workload-account/stacks/pipeline_stack/cdk_stack.py [0:0]
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
#####################################---START PREREQS---##########################################
# Create a new Code Commit Repo for holding workload code
source_repo = codecommit.Repository(
self,
'CodeCommitRepo',
repository_name = params['CODE_COMMIT_SOURCE_REPO_NAME'],
description = 'Workload repository'
)
# Source Repo ARN is going to be passed into the subsequent stacks that are created after this one.
self.source_repo_arn = source_repo.repository_arn
## Terraform workload statefile resource creation
# encryption key for terraform workload state file bucket
statefile_encryption_key = kms.Key(
self,
'WorkloadStatefileEncryptionKey',
#alias = core.Aws.STACK_NAME,
alias = 'workload/statefile',
description = 'Encryption key for workload statefile bucket',
enabled = True,
enable_key_rotation = True
)
# terraform workload state file bucket
statefile_bucket = s3.Bucket(
self,
'WorkloadStatefileS3Bucket',
access_control = s3.BucketAccessControl.PRIVATE,
bucket_name = params['WORKLOAD_STATEFILE_BUCKET_NAME_PREFIX']+core.Aws.ACCOUNT_ID+'-'+core.Aws.REGION,
encryption = s3.BucketEncryption.KMS,
encryption_key = statefile_encryption_key,
lifecycle_rules = [
s3.LifecycleRule(
enabled = True,
id = 'LccRule1-ExpireAllNoncurrentIn8Days',
noncurrent_version_expiration = core.Duration.days(8),
prefix = ''
)
],
public_read_access = False,
removal_policy = core.RemovalPolicy.DESTROY,
versioned = True
)
# CodePipeline artifact_bucket ecryption key
pipeline_encryption_key = kms.Key(
self,
'PipelineEncryptionKey',
#alias = core.Aws.STACK_NAME,
alias = 'codepipeline/workload',
description = 'Encryption key for workload codepipeline artifact_bucket',
enabled = True,
enable_key_rotation = True
)
print(core.Aws.ACCOUNT_ID)
# CodePipeline Bucket
pipeline_bucket = s3.Bucket(
self,
'CodePipelineS3Bucket',
access_control = s3.BucketAccessControl.PRIVATE,
bucket_name = 'pipeline-bucket-workload-'+core.Aws.ACCOUNT_ID,
encryption = s3.BucketEncryption.KMS,
encryption_key = pipeline_encryption_key,
lifecycle_rules = [
s3.LifecycleRule(
enabled = True,
id = 'LccRule1-ExpireAllNoncurrentIn8Days',
noncurrent_version_expiration = core.Duration.days(8),
prefix = ''
)
],
public_read_access = False,
removal_policy = core.RemovalPolicy.DESTROY,
versioned = True
)
# Retrieve cross account role from params
cross_account_role = params['COMPLIANCE_CODE']['CROSS_ACCOUNT_ROLE_ARN']
print(cross_account_role)
# IAM Role for CodePipeline
code_pipeline_role = iam.Role(
self,
'CodePipelineRole',
assumed_by = iam.ServicePrincipal('codepipeline.amazonaws.com')
)
# IAM Policy for CodePipeline
code_pipeline_policy = iam.Policy(
self,
'CodePipelinePolicy',
roles = [
code_pipeline_role
],
statements = [
iam.PolicyStatement(
sid = 'KmsAllowKeyUsage',
actions = [
'kms:DescribeKey',
'kms:GetKeyPolicy',
'kms:List*',
'kms:Encrypt',
'kms:Decrypt',
'kms:ReEncrypt*',
'kms:Generate*'
],
effect = iam.Effect.ALLOW,
resources = [
pipeline_encryption_key.key_arn
]
),
iam.PolicyStatement(
sid = 'CodeCommitRepoAccess',
actions = [
'codecommit:GetBranch',
'codecommit:GetCommit',
'codecommit:UploadArchive',
'codecommit:GetUploadArchiveStatus',
'codecommit:CancelUploadArchive'
],
effect = iam.Effect.ALLOW,
resources = [
source_repo.repository_arn
]
),
iam.PolicyStatement(
sid = 'PipelineBucketAccess',
actions = [
's3:GetBucket*',
's3:ListBucket*'
],
effect = iam.Effect.ALLOW,
resources = [
pipeline_bucket.bucket_arn
]
),
iam.PolicyStatement(
sid = 'PipelineBucketObjectAccess',
actions = [
's3:AbortMultipartUpload',
's3:GetObject*',
's3:PutObject*',
's3:DeleteObject*',
's3:RestoreObject',
's3:ListMultipartUploadParts'
],
effect = iam.Effect.ALLOW,
resources = [
pipeline_bucket.bucket_arn+'/*'
]
),
iam.PolicyStatement(
sid = 'PassRoleAccess',
actions = [
'iam:PassRole'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'BuilStartStopAccess',
actions = [
'codebuild:StartBuild',
'codebuild:BatchGetBuilds'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'AssumeDeploymentRolePolicy',
actions = [
'sts:AssumeRole'
],
effect = iam.Effect.ALLOW,
resources = [cross_account_role]
)
]
)
# IAM Role for CodeBuild projects
code_build_role = iam.Role(
self,
'CodeBuildRole',
assumed_by = iam.ServicePrincipal('codebuild.amazonaws.com')
)
# IAM Policy for CodeBuild Projects
code_build_policy = iam.Policy(
self,
'CodeBuildPolicy',
roles = [
code_build_role
],
statements = [
iam.PolicyStatement(
sid = 'KmsAllowKeyUsage',
actions = [
'kms:DescribeKey',
'kms:GetKeyPolicy',
'kms:List*',
'kms:Encrypt',
'kms:Decrypt',
'kms:ReEncrypt*',
'kms:Generate*',
'kms:TagResource',
'kms:UntagResource',
'kms:CreateKey',
'kms:GetKeyRotationStatus',
'kms:ScheduleKeyDeletion',
'kms:PutKeyPolicy'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'CloudWatchLogsPermissionsForAllCodeBuildProjects',
actions = [
'logs:*'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'S3BucketAccess',
actions = [
's3:GetBucket*',
's3:ListBucket*',
's3:CreateBucket',
's3:DeleteBucket',
's3:PutBucketTagging',
's3:PutLifecycleConfiguration',
's3:GetLifecycleConfiguration',
's3:GetEncryptionConfiguration',
's3:PutEncryptionConfiguration',
's3:GetAccelerateConfiguration',
's3:PutAccelerateConfiguration',
's3:GetReplicationConfiguration',
's3:PutReplicationConfiguration',
's3:ReplicateTags',
's3:GetBucketPolicy',
's3:PutBucketPolicy',
's3:PutBucketLifecycle',
's3:GetAccelerateConfiguration',
's3:GetObject',
's3:PutObject',
's3:DeleteObjectVersion',
's3:GetBucketLogging',
's3:PutBucketLogging'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'StateFileBucketObjectAccess',
actions = [
's3:GetObject*',
's3:PutObject*'
],
effect = iam.Effect.ALLOW,
resources = [
statefile_bucket.bucket_arn+'/*'
]
),
iam.PolicyStatement(
sid = 'CodeCommitAccessPolicy',
actions = [
'codecommit:*'
],
effect = iam.Effect.ALLOW,
resources = [
source_repo.repository_arn
]
),
iam.PolicyStatement(
sid = 'PassRoleAccess',
actions = [
'iam:PassRole',
'iam:CreateRole',
'iam:TagRole',
'iam:GetRole',
'iam:CreateInstanceProfile',
'iam:GetInstanceProfile',
'iam:DeleteInstanceProfile',
'iam:AddRoleToInstanceProfile',
'iam:ListInstanceProfilesForRole',
'iam:ListRolePolicies',
'iam:ListAttachedRolePolicies',
'iam:TagInstanceProfile',
'iam:RemoveRoleFromInstanceProfile',
'iam:DeleteRole'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'CodeBuildPermissions',
actions = [
'codebuild:Get*',
'codebuild:List*',
'codebuild:Describe*',
'codebuild:*Report*',
'codebuild:BatchPutTestCases'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'SnsPermissions',
actions = [
'sns:CreateTopic',
'sns:TagResource',
'sns:GetTopicAttributes',
'sns:ListTagsForResource',
'SNS:DeleteTopic'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'DlmPermissions',
actions = [
'dlm:*'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'CWPermissions',
actions = [
'cloudwatch:*',
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'CloudTrailPermissions',
actions = [
'cloudtrail:*',
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'LambdaPermissions',
actions = [
'lambda:*'
],
effect = iam.Effect.ALLOW,
resources = ['arn:aws:lambda:*:*:function:demo-lambda']
),
iam.PolicyStatement(
sid = 'ec2Permissions',
actions = [
'ec2:AuthorizeSecurityGroupEgress',
'ec2:AuthorizeSecurityGroupIngress',
'ec2:CreateSecurityGroup',
'ec2:DeleteSecurityGroup',
'ec2:DescribeSecurityGroups',
'ec2:RevokeSecurityGroupEgress',
'ec2:UpdateSecurityGroupRuleDescriptionsIngress',
'ec2:UpdateSecurityGroupRuleDescriptionsEgress',
'ec2:DescribeTags',
'ec2:CreateTags',
'ec2:DeleteTags',
'ec2:CreateVolume',
'ec2:DescribeVolume*',
'ec2:DescribeAvailabilityZones',
'ec2:CreateKeyPair',
'ec2:DeleteKeyPair',
'ec2:ImportKeyPair',
'ec2:DescribeKeyPairs',
'ec2:RunInstances',
'ec2:DescribeInstances',
'ec2:DescribeInstanceStatus',
'ec2:AssociateIamInstanceProfile',
'ec2:StartInstances',
'ec2:StopInstances',
'ec2:TerminateInstances',
'ec2:DescribeInstanceAttribute',
'ec2:DescribeVpcs',
'ec2:DescribeAccountAttributes',
'ec2:DescribeInstanceCreditSpecifications',
'ec2:GetDefaultCreditSpecification',
'ec2:ModifyDefaultCreditSpecification',
'ec2:ModifyInstanceCreditSpecification',
'ec2:DeleteVolume',
'ec2:DescribeNetworkInterfaceAttribute',
'ec2:DescribeNetworkInterfaces',
'ec2:DescribeNetworkInterfacePermissions',
'ec2:AttachNetworkInterface',
'ec2:DescribeNetworkInterfaces',
'ec2:DescribeNetworkInterfaceAttribute',
'ec2:DescribeNetworkInterfacePermissions',
'ec2:RevokeSecurityGroupIngress',
'ec2:DescribeImages'
],
effect = iam.Effect.ALLOW,
resources = ['*']
),
iam.PolicyStatement(
sid = 'AssumeCrossAccountRoleForCodePull',
actions = [
'sts:AssumeRole'
],
effect = iam.Effect.ALLOW,
resources = [cross_account_role]
)
]
)
# CodePipeline Encryption Key Policy
pipeline_encryption_key.add_to_resource_policy(
statement = iam.PolicyStatement(
sid = 'KmsAllowKeyAdministration',
actions = [
'kms:*'
],
effect = iam.Effect.ALLOW,
principals = [
iam.AccountRootPrincipal()
],
resources = ['*']
)
)
pipeline_encryption_key.add_to_resource_policy(
statement = iam.PolicyStatement(
sid = 'KmsAllowKeyUsage',
actions = [
'kms:Decrypt',
'kms:DescribeKey',
'kms:Encrypt',
'kms:GenerateDataKey',
'kms:GenerateDataKeyWithoutPlainText',
'kms:ReEncrypt',
'kms:ReEncryptTo',
'kms:ReEncryptFrom',
'kms:TagResource',
'kms:CreateKey'
],
effect = iam.Effect.ALLOW,
principals = [
iam.ArnPrincipal(
arn = code_pipeline_role.role_arn
)
],
resources = ['*']
)
)
# CodePipeline Bucket Policy
pipeline_bucket.add_to_resource_policy(
iam.PolicyStatement(
sid = 'CodePipelineUsage',
actions = [
's3:List*',
's3:Get*',
's3:Put*',
's3:Delete*',
's3:AbortMultipartUpload',
's3:RestoreObject',
's3:ListMultipartUploadParts'
],
effect = iam.Effect.ALLOW,
principals = [
iam.ArnPrincipal(
arn = code_pipeline_role.role_arn
)
],
resources = [
pipeline_bucket.bucket_arn,
pipeline_bucket.bucket_arn+'/*'
]
),
)
# workload statefile Encryption Key Policy
statefile_encryption_key.add_to_resource_policy(
statement = iam.PolicyStatement(
sid = 'KmsAllowKeyAdministration',
actions = [
'kms:*'
],
effect = iam.Effect.ALLOW,
principals = [
iam.AccountRootPrincipal()
],
resources = ['*']
)
)
statefile_encryption_key.add_to_resource_policy(
statement = iam.PolicyStatement(
sid = 'KmsAllowKeyUsage',
actions = [
'kms:Decrypt',
'kms:DescribeKey',
'kms:Encrypt',
'kms:GenerateDataKey',
'kms:GenerateDataKeyWithoutPlainText',
'kms:ReEncrypt',
'kms:ReEncryptTo',
'kms:ReEncryptFrom',
'kms:TagResource',
'kms:CreateKey'
],
effect = iam.Effect.ALLOW,
principals = [
iam.ArnPrincipal(
arn = code_build_role.role_arn
)
],
resources = ['*']
)
)
# workload statefile Bucket Policy
statefile_bucket.add_to_resource_policy(
iam.PolicyStatement(
sid = 'CodeBuildUsage',
actions = [
's3:List*',
's3:Get*',
's3:Put*',
's3:Delete*',
's3:AbortMultipartUpload',
's3:RestoreObject',
's3:ListMultipartUploadParts'
],
effect = iam.Effect.ALLOW,
principals = [
iam.ArnPrincipal(
arn = code_build_role.role_arn
)
],
resources = [
statefile_bucket.bucket_arn,
statefile_bucket.bucket_arn+'/*'
]
),
)
#####################################---END PREREQS---##########################################
# Create code build project for pulling compliance code from remote Security & Compliance repo and executing compliance run on workload terraform
code_build_compliance_run = codebuild.PipelineProject(
self,
'CodeBuildForComplianceRun',
build_spec = codebuild.BuildSpec.from_source_filename('buildspec-compliance.yml'),
description = 'CodeBuild project for pulling code from remote Security & Compliance repo and executing compliance run on workload terraform workload',
environment = codebuild.BuildEnvironment(
build_image = codebuild.LinuxBuildImage.from_code_build_image_id(
'aws/codebuild/standard:4.0'
),
compute_type = codebuild.ComputeType.SMALL
#environment_variables = {
# 'CROSS_ACCOUNT_ROLE': codebuild.BuildEnvironmentVariable(value=cross_account_role)
#}
),
project_name = 'cb-compliance-run-'+params['CODE_COMMIT_SOURCE_REPO_NAME']+'-'+params['CODE_COMMIT_SOURCE_REPO_BRANCH'],
role = code_build_role
)
# Create code build project for workload deployment
code_build_workload_deployment_run = codebuild.PipelineProject(
self,
'CodeBuildForWorkloadDeployment',
build_spec = codebuild.BuildSpec.from_source_filename('buildspec-workload-deploy.yml'),
description = 'CodeBuild project for deploying the terraform workload',
environment = codebuild.BuildEnvironment(
build_image = codebuild.LinuxBuildImage.from_code_build_image_id(
'aws/codebuild/standard:4.0'
),
compute_type = codebuild.ComputeType.SMALL
#environment_variables = {
# 'CROSS_ACCOUNT_ROLE': codebuild.BuildEnvironmentVariable(value=cross_account_role)
#}
),
project_name = 'cb-workload-deployment-run-'+params['CODE_COMMIT_SOURCE_REPO_NAME']+'-'+params['CODE_COMMIT_SOURCE_REPO_BRANCH'],
role = code_build_role
)
# Create CodePipeline for compliance check of terraform workload
pipeline = codepipeline.Pipeline(
self,
'WorkloadPipeline',
artifact_bucket = pipeline_bucket,
pipeline_name = 'pipeline-'+params['CODE_COMMIT_SOURCE_REPO_NAME']+'-'+params['CODE_COMMIT_SOURCE_REPO_BRANCH'],
role = code_pipeline_role
)
# Add CodeCommit source repo as the action
pipeline.add_stage(
stage_name = 'Source',
actions = [
codepipeline_actions.CodeCommitSourceAction(
action_name = "Source",
output = codepipeline.Artifact(artifact_name = 'SourceArtifact'),
repository = source_repo,
branch = params['CODE_COMMIT_SOURCE_REPO_BRANCH'],
trigger = codepipeline_actions.CodeCommitTrigger.EVENTS
)
]
)
# Add stage to pull compliance source code and run compliance check
tf_code_artifact_name_prefix = "tf_code_"
pull_tf_code_stage = pipeline.add_stage(stage_name = 'RunComplianceCheck')
#for tf_workload in params['TERRAFORM_APPLICATION_WORKLOAD_LIST']:
pull_tf_code_stage.add_action(
codepipeline_actions.CodeBuildAction(
input = codepipeline.Artifact(artifact_name = 'SourceArtifact'),
project = code_build_compliance_run,
environment_variables = {
'CROSS_ACCOUNT_ROLE': codebuild.BuildEnvironmentVariable(
value = cross_account_role,
type = codebuild.BuildEnvironmentVariableType.PLAINTEXT
),
'COMPLIANCE_REPO_URL': codebuild.BuildEnvironmentVariable(
value = params['COMPLIANCE_CODE']['GIT_REPO_URL'],
type = codebuild.BuildEnvironmentVariableType.PLAINTEXT
),
'WORLOAD_STATEFILE_BUCKET_NAME': codebuild.BuildEnvironmentVariable(
value = statefile_bucket.bucket_name,
type = codebuild.BuildEnvironmentVariableType.PLAINTEXT
)
},
outputs = [
codepipeline.Artifact(artifact_name = tf_code_artifact_name_prefix+params['COMPLIANCE_CODE']['ID'])
],
type = codepipeline_actions.CodeBuildActionType.BUILD,
action_name = 'RunCompliance_'+params['COMPLIANCE_CODE']['ID'],
run_order = 5
)
)
# Add stage to deploy workload
tf_code_artifact_name_prefix2 = "tf_code2_"
tf_workload_deploy_stage = pipeline.add_stage(stage_name = 'DeployWorkload')
tf_workload_deploy_stage.add_action(
codepipeline_actions.CodeBuildAction(
input = codepipeline.Artifact(artifact_name = 'SourceArtifact'),
project = code_build_workload_deployment_run,
environment_variables = {
'CROSS_ACCOUNT_ROLE': codebuild.BuildEnvironmentVariable(
value = cross_account_role,
type = codebuild.BuildEnvironmentVariableType.PLAINTEXT
),
'COMPLIANCE_REPO_URL': codebuild.BuildEnvironmentVariable(
value = params['COMPLIANCE_CODE']['GIT_REPO_URL'],
type = codebuild.BuildEnvironmentVariableType.PLAINTEXT
),
'WORLOAD_STATEFILE_BUCKET_NAME': codebuild.BuildEnvironmentVariable(
value = statefile_bucket.bucket_name,
type = codebuild.BuildEnvironmentVariableType.PLAINTEXT
)
},
outputs = [
codepipeline.Artifact(artifact_name = tf_code_artifact_name_prefix2+params['COMPLIANCE_CODE']['ID'])
],
type = codepipeline_actions.CodeBuildActionType.BUILD,
action_name = 'DeployWorkload_'+params['COMPLIANCE_CODE']['ID'],
run_order = 6
)
)
########################### List of Outputs ##########################
core.CfnOutput(
self,
'OutSourceRepoArn',
value = source_repo.repository_arn,
description = 'workload source Repository ARN',
export_name = 'WORKLOAD-SOURCE-REPO-ARN'
)
core.CfnOutput(
self,
'OutSourceRepoHttpUrl',
value = source_repo.repository_clone_url_http,
description = 'Workload source Repository Http URL',
export_name = 'WORKLOAD-SOURCE-REPO-HTTP-URL'
)
core.CfnOutput(
self,
'OutPipelineBucketName',
value = pipeline_bucket.bucket_name,
description = 'Pipeline Bucket Name',
export_name = 'PIPELINE-BUCKET-NAME'
)
core.CfnOutput(
self,
'OutStateFileBucketName',
value = statefile_bucket.bucket_name,
description = 'Terraform Backend StateFile Bucket Name',
export_name = 'STATEFILE-BUCKET-NAME'
)