# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 from aws_cdk.core import Stack, Construct, Stage from aws_cdk.pipelines import CdkPipeline, SimpleSynthAction, ShellScriptAction import aws_cdk.core as cdk import aws_cdk.aws_codepipeline as codepipeline import aws_cdk.aws_codepipeline_actions as codepipeline_actions import aws_cdk.aws_codecommit as codecommit import aws_cdk.aws_iam as iam from serverless_api_stack import ServerlessApiStack from lib.cognito_stack import CognitoStack class AppStage(Stage): def __init__(self, scope: Construct, id: str, cognito_stack_name="", **kwargs): super().__init__(scope, id, **kwargs) self.cognito_stack_name = cognito_stack_name cognito_stack = CognitoStack(self, cognito_stack_name) # CDK pipeline prefixed Cognito stack name with Pipeline name automatically, need to address it while passing name to the API stack api_stack = ServerlessApiStack(self, "Api", cognito_stack_name=id+"-"+cognito_stack_name) api_stack.add_dependency(cognito_stack) self.api_stack_name = cdk.CfnOutput(api_stack, 'stack_name', value=api_stack.stack_name) class ApiPipelineStack(Stack): def __init__(self, scope: Construct, id: str, **kwargs) -> None: super().__init__(scope, id, **kwargs) CODECOMMIT_REPO_NAME = cdk.CfnParameter(self, "CodeCommitRepoName", type="String", default="serverless-api-pipeline-cdk", description="CodeCommit repository with the project code").value_as_string PIPELINE_NAME = "serverless-api-pipeline-cdk" source_artifact = codepipeline.Artifact() cloud_assembly_artifact = codepipeline.Artifact() pipeline = CdkPipeline(self, "Pipeline", pipeline_name=PIPELINE_NAME, cloud_assembly_artifact=cloud_assembly_artifact, source_action=codepipeline_actions.CodeCommitSourceAction( action_name="CodeCommit", output=source_artifact, branch='main', trigger=codepipeline_actions.CodeCommitTrigger.POLL, repository=codecommit.Repository(self, 'ServerlessApiRepository', repository_name=CODECOMMIT_REPO_NAME) ), synth_action=SimpleSynthAction.standard_npm_synth( source_artifact=source_artifact, cloud_assembly_artifact=cloud_assembly_artifact, environment={'privileged': True}, install_command='cd ./serverless-rest-api/python-http-cdk; npm install -g aws-cdk; pip install -r requirements.txt; pip install -r ./src/api/requirements.txt ', synth_command='cdk synth --output $CODEBUILD_SRC_DIR/cdk.out' ) ) # Add testing stage to the pipeline and testing activity with permissions necessary to run integration tests testing_stage = AppStage(self, 'serverless-api-pipeline-cdk-Testing', cognito_stack_name='Cognito') pipeline_testing_stage = pipeline.add_application_stage(testing_stage) testing_action = ShellScriptAction( action_name='IntegrationTest', additional_artifacts=[source_artifact], commands=[ 'cd ./serverless-rest-api/python-http-cdk', 'pip install -r ./tests/requirements.txt', 'pip install -r ./src/api/requirements.txt', 'python -m pytest tests/integration -v' ], use_outputs={ 'TEST_APPLICATION_STACK_NAME': pipeline.stack_output(testing_stage.api_stack_name) }, ) pipeline_testing_stage.add_actions(testing_action) testing_action.project.add_to_role_policy(iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=[ 'cognito-idp:AdminDeleteUser', 'cognito-idp:AdminConfirmSignUp', 'cognito-idp:AdminAddUserToGroup' ], resources=[f'arn:aws:cognito-idp:{cdk.Aws.REGION}:{cdk.Aws.ACCOUNT_ID}:userpool/*'], ) ) testing_action.project.add_to_role_policy(iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=['secretsmanager:GetRandomPassword'], resources=['*'], ) ) testing_action.project.add_to_role_policy(iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=['dynamodb:*'], resources=[f'arn:aws:dynamodb:{cdk.Aws.REGION}:{cdk.Aws.ACCOUNT_ID}:table/{testing_stage.stage_name}*'], ) ) testing_action.project.add_to_role_policy(iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=['cloudformation:DescribeStacks'], resources=[ f'arn:aws:cloudformation:{cdk.Aws.REGION}:{cdk.Aws.ACCOUNT_ID}:stack/{testing_stage.stage_name}*/*', f'arn:aws:cloudformation:{cdk.Aws.REGION}:{cdk.Aws.ACCOUNT_ID}:stack/{testing_stage.cognito_stack_name}/*' ], ) ) # Create production deployment stage to the pipeline with manual approval action deployment_stage = AppStage(self, 'serverless-api-pipeline-cdk-Deployment', cognito_stack_name='Cognito') pipeline_deployment_stage = pipeline.add_application_stage(deployment_stage) pipeline_deployment_stage.add_actions( codepipeline_actions.ManualApprovalAction(action_name='ApproveProductionDeployment', run_order=1) )