Пример #1
0
    def setup_custom_authorizer_function(self, unique_id, code_asset,
                                         code_handler, description,
                                         environment, region) -> str:
        """
        Sets up the authorizer Lambda, and grants 'lambda:InvokeFunction' to the service principal
        'iot.amazonaws.com'

        :return: the ARN of the created function
        """
        merged_environment_vars = {
            "RESOURCE_ARN": f"arn:aws:iot:{region}:{self.account}:*"
        }
        merged_environment_vars.update(environment)
        authorizer_function = aws_lambda.Function(
            self,
            f"iot_custom_authorizer_function_{unique_id}",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset(code_asset),
            handler=code_handler,
            description=description,
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
            environment=merged_environment_vars)

        authorizer_function.grant_invoke(
            aws_iam.ServicePrincipal("iot.amazonaws.com"))
        return authorizer_function.function_arn
    def create_lambda(self, function_name: str,
                      custom_role: iam_.Role) -> lambda_.Function:
        if custom_role is None:
            custom_role: iam_.Role = self.create_default_role(function_name)

        return lambda_.Function(
            self,
            f"{self.id}-{function_name}",
            code=lambda_.Code.from_asset(
                lambda_dir,
                exclude=[
                    "*.test.py",
                    "requirements.txt",
                ],
            ),
            current_version_options=lambda_.VersionOptions(
                removal_policy=RemovalPolicy.DESTROY,
                retry_attempts=2,
            ),
            function_name=f"{self.id}-{function_name}",
            handler=f"{function_name}.lambda_handler",
            log_retention=RetentionDays.ONE_DAY,
            role=custom_role,
            runtime=lambda_.Runtime.PYTHON_3_8,
            timeout=Duration.seconds(900),
            tracing=lambda_.Tracing.DISABLED,
        )
Пример #3
0
    def setup_iot_endpoint_provider(self):
        describe_endpoint_policy = aws_iam.PolicyStatement(
            effect=aws_iam.Effect.ALLOW,
            actions=["iot:DescribeEndpoint"],
            resources=["*"],
        )

        provider_lambda = aws_lambda.SingletonFunction(
            self,
            "iot_data_ats_endpoint_provider_lambda",
            uuid="iot_data_ats_endpoint_provider_lambda_20200507150213",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset("custom_resources/iot_endpoint"),
            handler="iot_endpoint_provider.on_event",
            description="Returns iot:Data-ATS endpoint for this account",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
            initial_policy=[describe_endpoint_policy],
        )

        provider = custom_resources.Provider(self,
                                             "iot_data_ats_endpoint_provider",
                                             on_event_handler=provider_lambda)

        iot_endpoint = core.CustomResource(
            self,
            "iot_data_ats_endpoint",
            resource_type="Custom::IotDataAtsEndpoint",
            service_token=provider.service_token,
        )

        endpoint_address = iot_endpoint.get_att("EndpointAddress").to_string()

        self._parameters_to_save["iot_endpoint_address"] = endpoint_address
Пример #4
0
    def setup_custom_authorizer_function(self) -> str:
        """
        Sets up the authorizer Lambda, and grants 'lambda:InvokeFunction' to the service principal
        'iot.amazonaws.com'

        :return: the ARN of the created function
        """
        authorizer_function = aws_lambda.Function(
            self,
            "iot_custom_authorizer_function",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset(
                "custom_resources/iot_custom_authorizer_function"),
            handler="iot_custom_authorizer.handler",
            description=
            "Sample custom authorizer that allows or denies based on 'token' value",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
            environment={
                "RESOURCE_ARN": f"arn:aws:iot:{self.region}:{self.account}:*"
            },
        )
        authorizer_function.grant_invoke(
            aws_iam.ServicePrincipal("iot.amazonaws.com"))
        return authorizer_function.function_arn
Пример #5
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 bucket_name: str, cloudfront_distribution_id: str,
                 iam_construct: IAMConstruct,
                 secretsmanager_construct: SecretsManagerConstruct,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        self.credential_rotator = aws_lambda_python.PythonFunction(
            self,
            "credential_rotation_lambda",
            entry="cdk/credential_rotation/lambda_functions/src",
            index="handler.py",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            role=iam_construct.lambda_role,
            timeout=core.Duration.minutes(5),
            retry_attempts=1,
            description=
            "Credential rotation script for the AWS iOS SDK CircleCI pipeline",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
        )
        self.credential_rotator.add_environment(
            lambda_constants.CIRCLE_CI_IOS_SDK_API_TOKEN_ENV,
            secretsmanager_construct.circleci_aws_ios_sdk_api_key.
            secret_full_arn,
        )

        self.credential_rotator.add_environment(
            lambda_constants.CIRCLE_CI_IOS_SDK_SPM_API_TOKEN_ENV,
            secretsmanager_construct.circleci_aws_ios_sdk_spm_api_key.
            secret_full_arn,
        )

        self.credential_rotator.add_environment(
            lambda_constants.GITHUB_CREDENTIALS_SECRET_ENV,
            secretsmanager_construct.github_release_api_key.secret_full_arn,
        )

        self.credential_rotator.add_environment(
            lambda_constants.IAM_ROLE_ENV,
            iam_construct.circleci_release_role.role_arn,
        )

        self.credential_rotator.add_environment(
            lambda_constants.IAM_USERNAME_ENV,
            iam_construct.circleci_user.user_name)

        self.credential_rotator.add_environment(
            lambda_constants.GITHUB_PROJECT_PATH_ENV,
            "aws-amplify/aws-sdk-ios")

        self.credential_rotator.add_environment(
            lambda_constants.RELEASE_BUCKET_NAME_ENV, bucket_name)

        self.credential_rotator.add_environment(
            lambda_constants.RELEASE_CLOUDFRONT_DISTRIBUTION_ID_ENV,
            cloudfront_distribution_id)
Пример #6
0
    def create_custom_authorizer_signing_key_generic(
            self, unique_id, description, token_value) -> core.CustomResource:
        """
        Uses a Lambda to create an asymmetric key pair, since neither CFn nor CDK support that as of
        this writing (2020-05-09)
        https://github.com/aws-cloudformation/aws-cloudformation-coverage-roadmap/issues/337

        After creating the key, it signs the token value using the private key, and stores all of
        `token_value`, `token_value`'s signature, and the public key in the stack's parameter store.

        :return: the CustomResource for the signing key
        """
        create_authorizer_policy = aws_iam.PolicyStatement(
            effect=aws_iam.Effect.ALLOW,
            actions=[
                "kms:CreateKey", "kms:GetPublicKey", "kms:ScheduleKeyDeletion",
                "kms:Sign"
            ],
            resources=["*"],
        )
        provider_lambda = aws_lambda.SingletonFunction(
            self,
            f"iot_custom_authorizer_key_provider_lambda_{unique_id}",
            uuid=
            f"iot_custom_authorizer_key_provider_lambda_20200507150213_{unique_id}",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset(
                "custom_resources/iot_custom_authorizer_key_provider"),
            handler="iot_custom_authorizer_key_provider.on_event",
            description=description,
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
            initial_policy=[create_authorizer_policy],
        )

        provider = custom_resources.Provider(
            self,
            f"iot_custom_authorizer_key_provider_{unique_id}",
            on_event_handler=provider_lambda,
        )

        iot_custom_authorizer_key = core.CustomResource(
            self,
            f"iot_custom_authorizer_key_{unique_id}",
            resource_type="Custom::IoTCustomAuthorizer",
            service_token=provider.service_token,
            properties={"token_value": token_value},
        )

        return iot_custom_authorizer_key
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        # myDateTimeFunction lambda function
        my_datetime_lambda = _lambda.Function(
            self, 
            "my-datetime",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="myDateTimeFunction.handler",
            code=_lambda.Code.asset("./lambda"),
            current_version_options=_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.RETAIN, 
                retry_attempts=1
            )
        )

        my_datetime_lambda.add_to_role_policy(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,
                actions=["lambda:InvokeFunction"],
                resources=["*"]
            )
        )

        codedeploy.LambdaDeploymentGroup(
            self, 
            "datetime-lambda-deployment",
            alias=my_datetime_lambda.current_version.add_alias(
                "live"
            ),
            deployment_config=codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE
        )

        gw = _apigw.LambdaRestApi(
            self, 
            "Gateway",
            handler=my_datetime_lambda,
            description="Endpoint for a simple Lambda-powered web service"
        )

        # add an output with a well-known name to read it from the integ tests
        self.gw_url = gw.url
    def __init__(self, scope: core.Construct, id: str,
                 common_stack: CommonStack, **kwargs) -> None:

        super().__init__(scope, id, **kwargs)

        self._supported_in_region = self.is_service_supported_in_region()

        echo = aws_lambda.Function(
            self,
            "echo",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset("lambda"),
            handler="echo.handler",
            description=datetime.now().strftime("%d-%b-%Y (%H:%M:%S.%f)"),
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
        )

        version_alias_associated_version, version_alias_name = self.attach_alias_to_version(
            echo.current_version)

        echo2 = aws_lambda.Function(
            self,
            "echo2",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset("lambda"),
            handler="echo.handler",
        )

        self._parameters_to_save = {
            "echo_function_name": echo.function_name,
            "echo2_function_name": echo2.function_name,
            "version_alias_name": version_alias_name,
            "version_alias_associated_version":
            version_alias_associated_version,
        }
        self.save_parameters_in_parameter_store(platform=Platform.IOS)

        self._lambda_echo_function = echo

        common_stack.add_to_common_role_policies(self)
Пример #9
0
    def __init__(self, scope: core.Construct, id: str, table: dynamodb.Table, index_name: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        role = iam.Role(
            self, 'LambdaRole',
            assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name('service-role/AWSLambdaBasicExecutionRole'),
            ],
        )

        table.grant_read_data(role)

        xray = lambda_.LayerVersion(
            self, 'Xray',
            compatible_runtimes=[
                lambda_.Runtime.PYTHON_3_6,
                lambda_.Runtime.PYTHON_3_7,
                lambda_.Runtime.PYTHON_3_8,
            ],
            code=lambda_.Code.from_asset('src/xray'),
        )

        version_options = lambda_.VersionOptions(
            retry_attempts=0, # No retries
            provisioned_concurrent_executions=1,  # Avoid cold starts
        )

        list_function = lambda_.Function(
            self, 'List',
            runtime=lambda_.Runtime.PYTHON_3_8,
            code=lambda_.Code.from_asset('src/list'),
            handler='list.handler',
            role=role,
            tracing=lambda_.Tracing.ACTIVE,
            current_version_options=version_options,
            layers=[
                xray,
            ],
            environment={
                'TABLE_NAME': table.table_name,
                'INDEX_NAME': index_name,
            }
        )

        query_function = lambda_.Function(
            self, 'Query',
            runtime=lambda_.Runtime.PYTHON_3_8,
            code=lambda_.Code.from_asset('src/query'),
            handler='query.handler',
            role=role,
            tracing=lambda_.Tracing.ACTIVE,
            current_version_options=version_options,
            layers=[
                xray,
            ],
            environment={
                'TABLE_NAME': table.table_name
            }
        )

        api = apigateway.RestApi(
            self, 'StockHistory',
            endpoint_types=[
                apigateway.EndpointType.REGIONAL
            ],
            cloud_watch_role=True,
            deploy_options=apigateway.StageOptions(
                logging_level=apigateway.MethodLoggingLevel.ERROR,
                metrics_enabled=True,
                tracing_enabled=True,
            ),
        )

        stock_root_resource = api.root.add_resource('stock')
        stock_id_resource = stock_root_resource.add_resource('{ticker}')

        stock_root_resource.add_method(
            http_method='GET',
            integration=apigateway.LambdaIntegration(
                list_function.current_version,
                proxy=True,
            ),
        )

        stock_id_resource.add_method(
            http_method='GET',
            integration=apigateway.LambdaIntegration(
                query_function.current_version,
                proxy=True,
            ),
            request_parameters={
                'method.request.querystring.start': False,
                'method.request.querystring.end': False,
            },
        )

        self.api = api
Пример #10
0
    def setup_custom_authorizer_user_pass(self):
        custom_authorizer_name = self.custom_auth_user_pass_default_authorizer_name
        self._parameters_to_save[
            "custom_authorizer_user_pass_name"] = custom_authorizer_name
        token_key_name = "IoTTokenKeyName"
        self._parameters_to_save[
            "custom_authorizer_user_pass_token_key_name"] = token_key_name
        token_value = "allow"
        self._parameters_to_save[
            "custom_authorizer_user_pass_token_value"] = token_value
        self._parameters_to_save[
            "custom_authorizer_user_pass_username"] = self.custom_auth_user_pass_username
        self._parameters_to_save[
            "custom_authorizer_user_pass_password"] = self.custom_auth_user_pass_password

        iot_custom_authorizer_key_resource = self.create_custom_authorizer_signing_key_generic(
            "2",
            "Manages an asymmetric CMK and token signature for iot custom authorizer with username and password.",
            token_value,
        )

        custom_authorizer_token_signature = iot_custom_authorizer_key_resource.get_att(
            "custom_authorizer_token_signature").to_string()
        self._parameters_to_save[
            "custom_authorizer_user_pass_token_signature"] = custom_authorizer_token_signature

        # TODO: remove forcing of us-east-1 when enhanced custom authorizers are available in all regions
        # Force region to 'us-east-1' due to enhanced custom authorizers only available in this region
        authorizer_function_arn = self.setup_custom_authorizer_function(
            "2",
            "custom_resources/iot_custom_authorizer_user_pass_function",
            "iot_custom_authorizer_user_pass.handler",
            "Sample custom authorizer that allows or denies based on username and password",
            {
                "custom_auth_user_pass_username":
                self.custom_auth_user_pass_username,
                "custom_auth_user_pass_password":
                self.custom_auth_user_pass_password
            },
            "us-east-1",
        )
        create_authorizer_policy = aws_iam.PolicyStatement(
            effect=aws_iam.Effect.ALLOW,
            actions=[
                "iot:CreateAuthorizer",
                "iot:UpdateAuthorizer",
                "iot:DeleteAuthorizer",
                "iot:UpdateDomainConfiguration",
                "iot:CreateDomainConfiguration",
                "iot:DescribeDomainConfiguration",
                "iot:DeleteDomainConfiguration",
            ],
            resources=["*"],
        )
        provider_lambda = aws_lambda.SingletonFunction(
            self,
            "iot_custom_authorizer_user_pass_provider_lambda",
            uuid=
            "iot_custom_authorizer_user_pass_provider_lambda_20200727123737",
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset(
                "custom_resources/iot_custom_authorizer_user_pass_provider"),
            handler="iot_custom_authorizer_user_pass_provider.on_event",
            description=
            "Sets up an IoT custom authorizer for user password & required domain config due to beta status",
            environment={
                "custom_auth_user_pass_uuid":
                self.custom_auth_user_pass_uuid,
                "custom_auth_user_pass_default_authorizer_name":
                self.custom_auth_user_pass_default_authorizer_name,
                "custom_auth_user_pass_domain_configuration_name":
                self.custom_auth_user_pass_domain_configuration_name
            },
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
            initial_policy=[create_authorizer_policy],
        )

        provider = custom_resources.Provider(
            self,
            "iot_custom_authorizer_user_pass_provider",
            on_event_handler=provider_lambda)

        public_key = iot_custom_authorizer_key_resource.get_att(
            "custom_authorizer_public_key").to_string()

        iot_endpoint = core.CustomResource(
            self,
            "iot_custom_authorizer_user_pass",
            resource_type="Custom::IoTCustomAuthorizer",
            service_token=provider.service_token,
            properties={
                "authorizer_function_arn": authorizer_function_arn,
                "authorizer_name": custom_authorizer_name,
                "public_key": public_key,
                "token_key_name": token_key_name,
            },
        )
        endpoint_address = iot_endpoint.get_att(
            "BetaEndpointAddress").to_string()
        self._parameters_to_save[
            "iot_beta_endpoint_address"] = endpoint_address
Пример #11
0
    def setup_custom_authorizer(self):
        # These values are used in the custom authorizer setup, and exported to Parameter Store
        # for use by integration tests
        custom_authorizer_name = "iot_custom_authorizer"
        self._parameters_to_save[
            "custom_authorizer_name"] = custom_authorizer_name

        # Note: "key" is a bit overloaded here. In the context of the custom authorizer, "key name"
        # refers to the HTTP header field that the custom authorizer looks for a token value in.
        #
        # In the case of the custom authorizer key provider, the "key" is the KMS asymmetric CMK
        # used to sign the token value passed in the `token_key_name` header. In order to keep the
        # terminology consistent between client integ tests that are expecting to pass something for
        # a "key name" field, we'll let the ambiguity stand.
        token_key_name = "iot_custom_authorizer_token"
        self._parameters_to_save[
            "custom_authorizer_token_key_name"] = token_key_name

        token_value = "allow"
        self._parameters_to_save["custom_authorizer_token_value"] = token_value

        iot_custom_authorizer_key_resource = self.create_custom_authorizer_signing_key_generic(
            "1",
            "Manages an asymmetric CMK and token signature for iot custom authorizer.",
            token_value,
        )

        custom_authorizer_token_signature = iot_custom_authorizer_key_resource.get_att(
            "custom_authorizer_token_signature").to_string()
        self._parameters_to_save[
            "custom_authorizer_token_signature"] = custom_authorizer_token_signature

        authorizer_function_arn = self.setup_custom_authorizer_function(
            "1",
            "custom_resources/iot_custom_authorizer_function",
            "iot_custom_authorizer.handler",
            "Sample custom authorizer that allows or denies based on 'token' value",
            {},
            self.region,
        )

        create_authorizer_policy = aws_iam.PolicyStatement(
            effect=aws_iam.Effect.ALLOW,
            actions=[
                "iot:CreateAuthorizer", "iot:UpdateAuthorizer",
                "iot:DeleteAuthorizer"
            ],
            resources=["*"],
        )
        provider_lambda = aws_lambda.SingletonFunction(
            self,
            "iot_custom_authorizer_provider_lambda",
            uuid=self.custom_auth_user_pass_uuid,
            runtime=aws_lambda.Runtime.PYTHON_3_7,
            code=aws_lambda.Code.asset(
                "custom_resources/iot_custom_authorizer_provider"),
            handler="iot_custom_authorizer_provider.on_event",
            description="Sets up an IoT custom authorizer",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
            initial_policy=[create_authorizer_policy],
        )

        provider = custom_resources.Provider(self,
                                             "iot_custom_authorizer_provider",
                                             on_event_handler=provider_lambda)

        public_key = iot_custom_authorizer_key_resource.get_att(
            "custom_authorizer_public_key").to_string()

        core.CustomResource(
            self,
            "iot_custom_authorizer",
            resource_type="Custom::IoTCustomAuthorizer",
            service_token=provider.service_token,
            properties={
                "authorizer_function_arn": authorizer_function_arn,
                "authorizer_name": custom_authorizer_name,
                "public_key": public_key,
                "token_key_name": token_key_name,
            },
        )
Пример #12
0
    def create_custom_auth_lambda_configuration(
        self, ) -> aws_cognito.CfnUserPool.LambdaConfigProperty:
        cognito_service_principal = aws_iam.ServicePrincipal(
            "cognito-idp.amazonaws.com")
        create_auth_challenge = aws_lambda.Function(
            self,
            "custom_auth_lambda_create_auth_challenge",
            runtime=aws_lambda.Runtime.NODEJS_10_X,
            code=aws_lambda.Code.asset("custom_resources/custom_auth"),
            handler="create_auth_challenge.handler",
            description="custom auth: create_auth_challenge",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
        )
        create_auth_challenge.add_permission(
            "create_auth_challenge_invoke_permission",
            principal=cognito_service_principal)

        define_auth_challenge = aws_lambda.Function(
            self,
            "custom_auth_lambda_define_auth_challenge",
            runtime=aws_lambda.Runtime.NODEJS_10_X,
            code=aws_lambda.Code.asset("custom_resources/custom_auth"),
            handler="define_auth_challenge.handler",
            description="custom auth: define_auth_challenge",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
        )
        define_auth_challenge.add_permission(
            "define_auth_challenge_invoke_permission",
            principal=cognito_service_principal)

        pre_sign_up = aws_lambda.Function(
            self,
            "custom_auth_lambda_pre_sign_up",
            runtime=aws_lambda.Runtime.NODEJS_10_X,
            code=aws_lambda.Code.asset("custom_resources/custom_auth"),
            handler="pre_sign_up.handler",
            description="custom auth: pre_sign_up",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
        )
        pre_sign_up.add_permission("pre_sign_up_invoke_permission",
                                   principal=cognito_service_principal)

        verify_auth_challenge_response = aws_lambda.Function(
            self,
            "custom_auth_lambda_verify_auth_challenge_response",
            runtime=aws_lambda.Runtime.NODEJS_10_X,
            code=aws_lambda.Code.asset("custom_resources/custom_auth"),
            handler="verify_auth_challenge_response.handler",
            description="custom auth: verify_auth_challenge_response",
            current_version_options=aws_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.DESTROY),
        )
        verify_auth_challenge_response.add_permission(
            "verify_auth_challenge_response_invoke_permission",
            principal=cognito_service_principal)

        return aws_cognito.CfnUserPool.LambdaConfigProperty(
            create_auth_challenge=create_auth_challenge.function_arn,
            define_auth_challenge=define_auth_challenge.function_arn,
            pre_sign_up=pre_sign_up.function_arn,
            verify_auth_challenge_response=verify_auth_challenge_response.
            function_arn,
        )
Пример #13
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        env = kwargs['env']

        work_dir = pathlib.Path(__file__).parents[1]

        # These below steps allows to reuse ecs cluster which is aleady creatd by shared stack

        # Get cluster name from ssm parameter
        cluster_name = ssm.StringParameter.from_string_parameter_name(
            self,
            "GetClusterName",
            string_parameter_name="/dev/compute/container/ecs-cluster-name"
        ).string_value

        vpc_az = ssm.StringListParameter.from_string_list_parameter_name(
            self,
            "GetVpcAz",
            string_list_parameter_name="/dev/network/vpc/vpc-az"
        ).string_list_value

        # using string instead of stringlist because of subnets parsing issue
        vpc_public_subnets_1 = ssm.StringParameter.from_string_parameter_name(
            self,
            "GetVpcPublicSubnets1",
            string_parameter_name="/dev/network/vpc/vpc-public-subnets-1"
        ).string_value

        vpc_public_subnets_2 = ssm.StringParameter.from_string_parameter_name(
            self,
            "GetVpcPublicSubnets2",
            string_parameter_name="/dev/network/vpc/vpc-public-subnets-2"
        ).string_value

        vpc_id = ssm.StringParameter.from_string_parameter_name(
            self, "GetVpcId",
            string_parameter_name="/dev/network/vpc/vpc-id").string_value

        ec2_vpc = ec2.Vpc.from_vpc_attributes(
            self,
            "GetVpc",
            availability_zones=vpc_az,
            vpc_id=vpc_id,
            public_subnet_ids=[vpc_public_subnets_1, vpc_public_subnets_2])

        # Get security group id from ssm parameter
        security_group_id = ssm.StringParameter.from_string_parameter_name(
            self,
            "GetSgId",
            string_parameter_name="/dev/network/vpc/security-group-id"
        ).string_value

        # Get security group from lookup
        ec2_sgp = ec2.SecurityGroup.from_security_group_id(
            self, "GetSgp", security_group_id=security_group_id)

        # myDateTimeFunction lambda function
        my_datetime_lambda = _lambda.Function(
            self,
            "my-datetime",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="myDateTimeFunction.handler",
            code=_lambda.Code.asset("./lambda"),
            current_version_options=_lambda.VersionOptions(
                removal_policy=core.RemovalPolicy.RETAIN, retry_attempts=1))

        my_datetime_lambda.add_to_role_policy(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                actions=["lambda:InvokeFunction"],
                                resources=["*"]))

        # beforeAllowTraffic lambda function
        pre_traffic_lambda = _lambda.Function(
            self,
            "pre-traffic",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="beforeAllowTraffic.handler",
            code=_lambda.Code.asset("./lambda"),
            environment=dict(
                NewVersion=my_datetime_lambda.current_version.function_arn))

        pre_traffic_lambda.add_to_role_policy(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,
                actions=["codedeploy:PutLifecycleEventHookExecutionStatus"],
                resources=["*"]))

        pre_traffic_lambda.add_to_role_policy(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                actions=["lambda:InvokeFunction"],
                                resources=["*"]))

        # afterAllowTraffic lambda function
        post_traffic_lambda = _lambda.Function(
            self,
            "post-traffic",
            runtime=_lambda.Runtime.NODEJS_12_X,
            handler="afterAllowTraffic.handler",
            code=_lambda.Code.asset("./lambda"),
            environment=dict(
                NewVersion=my_datetime_lambda.current_version.function_arn))

        post_traffic_lambda.add_to_role_policy(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,
                actions=["codedeploy:PutLifecycleEventHookExecutionStatus"],
                resources=["*"]))

        post_traffic_lambda.add_to_role_policy(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                actions=["lambda:InvokeFunction"],
                                resources=["*"]))

        # create a cloudwatch event rule
        rule = events.Rule(
            self,
            "CanaryRule",
            schedule=events.Schedule.expression("rate(10 minutes)"),
            targets=[
                events_targets.LambdaFunction(
                    my_datetime_lambda.current_version)
            ],
        )

        # create a cloudwatch alarm based on the lambda erros metrics
        alarm = cloudwatch.Alarm(
            self,
            "CanaryAlarm",
            metric=my_datetime_lambda.current_version.metric_invocations(),
            threshold=0,
            evaluation_periods=2,
            datapoints_to_alarm=2,
            treat_missing_data=cloudwatch.TreatMissingData.IGNORE,
            period=core.Duration.minutes(5),
            alarm_name="CanaryAlarm")

        lambda_deployment_group = codedeploy.LambdaDeploymentGroup(
            self,
            "datetime-lambda-deployment",
            alias=my_datetime_lambda.current_version.add_alias("live"),
            deployment_config=codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE,
            alarms=[alarm],
            auto_rollback=codedeploy.AutoRollbackConfig(
                deployment_in_alarm=True),
            pre_hook=pre_traffic_lambda,
            post_hook=post_traffic_lambda)

        # Pass vpc, sgp and ecs cluster name to get ecs cluster info
        ecs_cluster = ecs.Cluster.from_cluster_attributes(
            self,
            "GetEcsCluster",
            cluster_name=cluster_name,
            vpc=ec2_vpc,
            security_groups=[ec2_sgp])

        # Fargate Service
        task_definition = ecs.FargateTaskDefinition(
            self,
            "TaskDef",
            memory_limit_mib=512,
            cpu=256,
        )

        container = task_definition.add_container(
            "web",
            image=ecs.ContainerImage.from_asset(
                os.path.join(work_dir, "container")),
            # Built custom health check for your application specific
            # and add them here. Ex: Pingcheck, Database etc.
            health_check=ecs.HealthCheck(command=["CMD-SHELL", "echo"]),
            # environment=dict(name="latest")
        )

        port_mapping = ecs.PortMapping(container_port=8000,
                                       protocol=ecs.Protocol.TCP)

        container.add_port_mappings(port_mapping)

        # Create Fargate Service
        # Current limitation: Blue/Green deployment
        # https://github.com/aws/aws-cdk/issues/1559
        service = ecs.FargateService(
            self,
            "Service",
            cluster=ecs_cluster,
            task_definition=task_definition,
            assign_public_ip=True,
            deployment_controller=ecs.DeploymentController(
                type=ecs.DeploymentControllerType.ECS),
            desired_count=2,
            min_healthy_percent=50)

        # Create Application LoadBalancer
        lb = elbv2.ApplicationLoadBalancer(self,
                                           "LB",
                                           vpc=ec2_vpc,
                                           internet_facing=True)

        # Add listener to the LB
        listener = lb.add_listener("Listener", port=80, open=True)

        # Default to Lambda
        listener.add_targets(
            "Lambda", targets=[elb_targets.LambdaTarget(my_datetime_lambda)])

        # Additionally route to container
        listener.add_targets("Fargate",
                             port=8000,
                             path_pattern="/container",
                             priority=10,
                             targets=[service])

        # add an output with a well-known name to read it from the integ tests
        self.load_balancer_dns_name = lb.load_balancer_dns_name