in packages/aws-cdk-lib/pipelines/lib/private/application-security-check.ts [53:160]
constructor(scope: Construct, id: string, props: ApplicationSecurityCheckProps) {
super(scope, id);
Tags.of(props.codePipeline).add('SECURITY_CHECK', 'ALLOW_APPROVE', {
includeResourceTypes: ['AWS::CodePipeline::Pipeline'],
});
this.preApproveLambda = new ApproveLambdaFunction(this, 'CDKPipelinesAutoApprove', {
timeout: Duration.minutes(5),
});
this.preApproveLambda.addToRolePolicy(new iam.PolicyStatement({
actions: ['codepipeline:GetPipelineState', 'codepipeline:PutApprovalResult'],
conditions: {
StringEquals: {
'aws:ResourceTag/SECURITY_CHECK': 'ALLOW_APPROVE',
},
},
resources: ['*'],
}));
const invokeLambda =
'aws lambda invoke' +
` --function-name ${this.preApproveLambda.functionName}` +
' --invocation-type Event' +
' --cli-binary-format raw-in-base64-out' +
' --payload "$payload"' +
' lambda.out';
const message = [
'An upcoming change would broaden security changes in $PIPELINE_NAME.',
'Review and approve the changes in CodePipeline to proceed with the deployment.',
'',
'Review the changes in CodeBuild:',
'',
'$LINK',
'',
'Approve the changes in CodePipeline (stage $STAGE_NAME, action $ACTION_NAME):',
'',
'$PIPELINE_LINK',
];
const publishNotification =
'aws sns publish' +
' --topic-arn $NOTIFICATION_ARN' +
' --subject "$NOTIFICATION_SUBJECT"' +
` --message "${message.join('\n')}"`;
this.cdkDiffProject = new codebuild.Project(this, 'CDKSecurityCheck', {
environment: {
buildImage: CDKP_DEFAULT_CODEBUILD_IMAGE,
},
buildSpec: codebuild.BuildSpec.fromObject({
version: 0.2,
phases: {
build: {
commands: [
'npm install -g aws-cdk',
// $CODEBUILD_INITIATOR will always be Code Pipeline and in the form of:
// "codepipeline/example-pipeline-name-Xxx"
'export PIPELINE_NAME="$(node -pe \'`${process.env.CODEBUILD_INITIATOR}`.split("/")[1]\')"',
'payload="$(node -pe \'JSON.stringify({ "PipelineName": process.env.PIPELINE_NAME, "StageName": process.env.STAGE_NAME, "ActionName": process.env.ACTION_NAME })\' )"',
// ARN: "arn:aws:codebuild:$region:$account_id:build/$project_name:$project_execution_id$"
'ARN=$CODEBUILD_BUILD_ARN',
'REGION="$(node -pe \'`${process.env.ARN}`.split(":")[3]\')"',
'ACCOUNT_ID="$(node -pe \'`${process.env.ARN}`.split(":")[4]\')"',
'PROJECT_NAME="$(node -pe \'`${process.env.ARN}`.split(":")[5].split("/")[1]\')"',
'PROJECT_ID="$(node -pe \'`${process.env.ARN}`.split(":")[6]\')"',
// Manual Approval adds 'http/https' to the resolved link
'export LINK="https://$REGION.console.aws.amazon.com/codesuite/codebuild/$ACCOUNT_ID/projects/$PROJECT_NAME/build/$PROJECT_NAME:$PROJECT_ID/?region=$REGION"',
'export PIPELINE_LINK="https://$REGION.console.aws.amazon.com/codesuite/codepipeline/pipelines/$PIPELINE_NAME/view?region=$REGION"',
// Run invoke only if cdk diff passes (returns exit code 0)
// 0 -> true, 1 -> false
ifElse({
condition: 'cdk diff -a . --security-only --fail $STAGE_PATH/\\*',
thenStatements: [
invokeLambda,
'export MESSAGE="No security-impacting changes detected."',
],
elseStatements: [
`[ -z "\${NOTIFICATION_ARN}" ] || ${publishNotification}`,
'export MESSAGE="Deployment would make security-impacting changes. Click the link below to inspect them, then click Approve if all changes are expected."',
],
}),
],
},
},
env: {
'exported-variables': [
'LINK',
'MESSAGE',
],
},
}),
});
// this is needed to check the status the stacks when doing `cdk diff`
this.cdkDiffProject.addToRolePolicy(new iam.PolicyStatement({
actions: ['sts:AssumeRole'],
resources: ['*'],
conditions: {
'ForAnyValue:StringEquals': {
'iam:ResourceTag/aws-cdk:bootstrap-role': ['deploy'],
},
},
}));
this.preApproveLambda.grantInvoke(this.cdkDiffProject);
}