def __init__(
        self,
        scope: Construct,
        id: str,
        function_id: str,
        bucket: S3Bucket,
        config: BaseClusterConfig,
        execution_role: iam.CfnRole,
        handler_func: str,
        timeout: int = 900,
    ):
        super().__init__(scope, id)

        function_name = f"pcluster-{function_id}-{self._stack_unique_id()}"

        self.log_group = logs.CfnLogGroup(
            scope,
            f"{function_id}FunctionLogGroup",
            log_group_name=f"/aws/lambda/{function_name}",
            retention_in_days=get_cloud_watch_logs_retention_days(config),
        )
        self.log_group.cfn_options.deletion_policy = get_log_group_deletion_policy(
            config)

        self.lambda_func = awslambda.CfnFunction(
            scope,
            f"{function_id}Function",
            function_name=function_name,
            code=awslambda.CfnFunction.CodeProperty(
                s3_bucket=bucket.name,
                s3_key=
                f"{bucket.artifact_directory}/custom_resources/artifacts.zip",
            ),
            handler=f"{handler_func}.handler",
            memory_size=128,
            role=execution_role,
            runtime="python3.8",
            timeout=timeout,
        )
    def _add_lambda_cleanup(self, policy_statements, build_tags):
        lambda_cleanup_execution_role = None
        if self.custom_cleanup_lambda_role:
            execution_role = self.custom_cleanup_lambda_role
        else:
            # LambdaCleanupPolicies
            self._add_resource_delete_policy(
                policy_statements,
                ["cloudformation:DeleteStack"],
                [
                    self.format_arn(
                        service="cloudformation",
                        resource="stack",
                        resource_name="{0}/{1}".format(
                            self.image_id, self._stack_unique_id()),
                    )
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                ["ec2:CreateTags"],
                [
                    self.format_arn(
                        service="ec2",
                        account="",
                        resource="image",
                        region=region,
                        resource_name="*",
                    ) for region in self._get_distribution_regions()
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                ["tag:TagResources"],
                ["*"],
            )

            self._add_resource_delete_policy(
                policy_statements,
                [
                    "iam:DetachRolePolicy", "iam:DeleteRole",
                    "iam:DeleteRolePolicy"
                ],
                [
                    self.format_arn(
                        service="iam",
                        resource="role",
                        region="",
                        resource_name="{0}/{1}".format(
                            IAM_ROLE_PATH.strip("/"),
                            self._build_resource_name(
                                IMAGEBUILDER_RESOURCE_NAME_PREFIX + "Cleanup"),
                        ),
                    )
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                ["lambda:DeleteFunction", "lambda:RemovePermission"],
                [
                    self.format_arn(
                        service="lambda",
                        resource="function",
                        sep=":",
                        resource_name=self._build_resource_name(
                            IMAGEBUILDER_RESOURCE_NAME_PREFIX),
                    )
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                ["logs:DeleteLogGroup"],
                [
                    self.format_arn(
                        service="logs",
                        resource="log-group",
                        sep=":",
                        resource_name="/aws/lambda/{0}:*".format(
                            self._build_resource_name(
                                IMAGEBUILDER_RESOURCE_NAME_PREFIX)),
                    )
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                ["iam:RemoveRoleFromInstanceProfile"],
                [
                    self.format_arn(
                        service="iam",
                        resource="instance-profile",
                        region="",
                        resource_name="{0}/{1}".format(
                            IAM_ROLE_PATH.strip("/"),
                            self._build_resource_name(
                                IMAGEBUILDER_RESOURCE_NAME_PREFIX),
                        ),
                    )
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                ["iam:DetachRolePolicy", "iam:DeleteRolePolicy"],
                [
                    self.format_arn(
                        service="iam",
                        resource="role",
                        region="",
                        resource_name="{0}/{1}".format(
                            IAM_ROLE_PATH.strip("/"),
                            self._build_resource_name(
                                IMAGEBUILDER_RESOURCE_NAME_PREFIX),
                        ),
                    )
                ],
            )

            self._add_resource_delete_policy(
                policy_statements,
                [
                    "SNS:GetTopicAttributes", "SNS:DeleteTopic",
                    "SNS:Unsubscribe"
                ],
                [
                    self.format_arn(
                        service="sns",
                        resource="{0}".format(
                            self._build_resource_name(
                                IMAGEBUILDER_RESOURCE_NAME_PREFIX)),
                    )
                ],
            )

            policy_document = iam.PolicyDocument(statements=policy_statements)
            managed_lambda_policy = [
                Fn.sub(
                    "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
                ),
            ]

            # LambdaCleanupExecutionRole
            lambda_cleanup_execution_role = iam.CfnRole(
                self,
                "DeleteStackFunctionExecutionRole",
                managed_policy_arns=managed_lambda_policy,
                assume_role_policy_document=get_assume_role_policy_document(
                    "lambda.amazonaws.com"),
                path=IAM_ROLE_PATH,
                policies=[
                    iam.CfnRole.PolicyProperty(
                        policy_document=policy_document,
                        policy_name="LambdaCleanupPolicy",
                    ),
                ],
                tags=build_tags,
                role_name=self._build_resource_name(
                    IMAGEBUILDER_RESOURCE_NAME_PREFIX + "Cleanup"),
            )

            execution_role = lambda_cleanup_execution_role.attr_arn

        # LambdaCleanupEnv
        lambda_env = awslambda.CfnFunction.EnvironmentProperty(
            variables={"IMAGE_STACK_ARN": self.stack_id})

        # LambdaCWLogGroup
        lambda_log = logs.CfnLogGroup(
            self,
            "DeleteStackFunctionLog",
            log_group_name="/aws/lambda/{0}".format(
                self._build_resource_name(IMAGEBUILDER_RESOURCE_NAME_PREFIX)),
        )

        # LambdaCleanupFunction
        lambda_cleanup = awslambda.CfnFunction(
            self,
            "DeleteStackFunction",
            function_name=self._build_resource_name(
                IMAGEBUILDER_RESOURCE_NAME_PREFIX),
            code=awslambda.CfnFunction.CodeProperty(
                s3_bucket=self.config.custom_s3_bucket
                or S3Bucket.get_bucket_name(
                    AWSApi.instance().sts.get_account_id(), get_region()),
                s3_key=self.bucket.get_object_key(S3FileType.CUSTOM_RESOURCES,
                                                  "artifacts.zip"),
            ),
            handler="delete_image_stack.handler",
            memory_size=128,
            role=execution_role,
            runtime="python3.8",
            timeout=900,
            environment=lambda_env,
            tags=build_tags,
        )
        permission = awslambda.CfnPermission(
            self,
            "DeleteStackFunctionPermission",
            action="lambda:InvokeFunction",
            principal="sns.amazonaws.com",
            function_name=lambda_cleanup.attr_arn,
            source_arn=Fn.ref("BuildNotificationTopic"),
        )
        lambda_cleanup.add_depends_on(lambda_log)

        return lambda_cleanup, permission, lambda_cleanup_execution_role, lambda_log
Example #3
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        current_directory = os.path.realpath(
            os.path.join(os.getcwd(), os.path.dirname(__file__)))
        allowed_values = yaml.load(open(
            os.path.join(current_directory, "..", "..",
                         "allowed_values.yaml")),
                                   Loader=yaml.SafeLoader)
        ami_mapping = {"AMI": {"OEJITSI": AMI_NAME}}
        for region in generated_ami_ids.keys():
            ami_mapping[region] = {"OEJITSI": generated_ami_ids[region]}
        aws_ami_region_map = core.CfnMapping(self,
                                             "AWSAMIRegionMap",
                                             mapping=ami_mapping)

        # utility function to parse the unique id from the stack id for
        # shorter resource names  using cloudformation functions
        def append_stack_uuid(name):
            return core.Fn.join("-", [
                name,
                core.Fn.select(
                    0,
                    core.Fn.split(
                        "-",
                        core.Fn.select(2, core.Fn.split(
                            "/", core.Aws.STACK_ID))))
            ])

        #
        # PARAMETERS
        #

        cidr_block_param = core.CfnParameter(
            self,
            "IngressCidrBlock",
            allowed_pattern="((\d{1,3})\.){3}\d{1,3}/\d{1,2}",
            default="0.0.0.0/0",
            description=
            "Required: A CIDR block to restrict access to the Jitsi application. Leave as 0.0.0.0/0 to allow public access from internet."
        )
        ec2_instance_type_param = core.CfnParameter(
            self,
            "InstanceType",
            allowed_values=allowed_values["allowed_instance_types"],
            default="t3.xlarge",
            description=
            "Required: The EC2 instance type for the application Auto Scaling Group."
        )
        jitsi_hostname_param = core.CfnParameter(
            self,
            "JitsiHostname",
            description=
            "Required: The hostname to access Jitsi. E.G. 'jitsi.internal.mycompany.com'"
        )
        jitsi_interface_app_name_param = core.CfnParameter(
            self,
            "JitsiInterfaceAppName",
            default="Jitsi Meet",
            description=
            "Optional: Customize the app name on the Jitsi interface.")
        jitsi_interface_default_remote_display_name_param = core.CfnParameter(
            self,
            "JitsiInterfaceDefaultRemoteDisplayName",
            default="Fellow Jitster",
            description=
            "Optional: Customize the default display name for Jitsi users.")
        jitsi_interface_native_app_name_param = core.CfnParameter(
            self,
            "JitsiInterfaceNativeAppName",
            default="Jitsi Meet",
            description=
            "Optional: Customize the native app name on the Jitsi interface.")
        jitsi_interface_show_brand_watermark_param = core.CfnParameter(
            self,
            "JitsiInterfaceShowBrandWatermark",
            allowed_values=["true", "false"],
            default="true",
            description=
            "Optional: Display the watermark logo image in the upper left corner."
        )
        jitsi_interface_show_watermark_for_guests_param = core.CfnParameter(
            self,
            "JitsiInterfaceShowWatermarkForGuests",
            allowed_values=["true", "false"],
            default="true",
            description=
            "Optional: Display the watermark logo image in the upper left corner for guest users. This can be set to override the general setting behavior for guest users."
        )
        jitsi_interface_brand_watermark_param = core.CfnParameter(
            self,
            "JitsiInterfaceBrandWatermark",
            default="",
            description=
            "Optional: Provide a URL to a PNG image to be used as the brand watermark logo image in the upper right corner. File should be publically available for download."
        )
        jitsi_interface_brand_watermark_link_param = core.CfnParameter(
            self,
            "JitsiInterfaceBrandWatermarkLink",
            default="http://jitsi.org",
            description=
            "Optional: Provide a link destination for the brand watermark logo image in the upper right corner."
        )
        jitsi_interface_watermark_param = core.CfnParameter(
            self,
            "JitsiInterfaceWatermark",
            default="",
            description=
            "Optional: Provide a URL to a PNG image to be used as the watermark logo image in the upper left corner. File should be publically available for download."
        )
        jitsi_interface_watermark_link_param = core.CfnParameter(
            self,
            "JitsiInterfaceWatermarkLink",
            default="http://jitsi.org",
            description=
            "Optional: Provide a link destination for the Jitsi watermark logo image in the upper left corner."
        )
        route_53_hosted_zone_name_param = core.CfnParameter(
            self,
            "Route53HostedZoneName",
            description=
            "Required: Route 53 Hosted Zone name in which a DNS record will be created by this template. Must already exist and be the domain part of the Jitsi Hostname parameter, without trailing dot. E.G. 'internal.mycompany.com'"
        )
        notification_email_param = core.CfnParameter(
            self,
            "NotificationEmail",
            default="",
            description=
            "Optional: Specify an email address to get emails about deploys, Let's Encrypt, and other system events."
        )

        #
        # CONDITIONS
        #

        notification_email_exists_condition = core.CfnCondition(
            self,
            "NotificationEmailExistsCondition",
            expression=core.Fn.condition_not(
                core.Fn.condition_equals(notification_email_param.value, "")))

        #
        # RESOURCES
        #

        # vpc
        vpc = Vpc(self, "Vpc")

        # sns
        sns_notification_topic = aws_sns.CfnTopic(
            self,
            "NotificationTopic",
            topic_name="{}-notifications".format(core.Aws.STACK_NAME))
        sns_notification_subscription = aws_sns.CfnSubscription(
            self,
            "NotificationSubscription",
            protocol="email",
            topic_arn=sns_notification_topic.ref,
            endpoint=notification_email_param.value_as_string)
        sns_notification_subscription.cfn_options.condition = notification_email_exists_condition
        iam_notification_publish_policy = aws_iam.PolicyDocument(statements=[
            aws_iam.PolicyStatement(effect=aws_iam.Effect.ALLOW,
                                    actions=["sns:Publish"],
                                    resources=[sns_notification_topic.ref])
        ])

        # cloudwatch
        app_log_group = aws_logs.CfnLogGroup(
            self, "JitsiAppLogGroup", retention_in_days=TWO_YEARS_IN_DAYS)
        app_log_group.cfn_options.update_replace_policy = core.CfnDeletionPolicy.RETAIN
        app_log_group.cfn_options.deletion_policy = core.CfnDeletionPolicy.RETAIN
        system_log_group = aws_logs.CfnLogGroup(
            self, "JitsiSystemLogGroup", retention_in_days=TWO_YEARS_IN_DAYS)
        system_log_group.cfn_options.update_replace_policy = core.CfnDeletionPolicy.RETAIN
        system_log_group.cfn_options.deletion_policy = core.CfnDeletionPolicy.RETAIN

        # iam
        iam_jitsi_instance_role = aws_iam.CfnRole(
            self,
            "JitsiInstanceRole",
            assume_role_policy_document=aws_iam.PolicyDocument(statements=[
                aws_iam.PolicyStatement(
                    effect=aws_iam.Effect.ALLOW,
                    actions=["sts:AssumeRole"],
                    principals=[aws_iam.ServicePrincipal("ec2.amazonaws.com")])
            ]),
            policies=[
                aws_iam.CfnRole.PolicyProperty(
                    policy_document=aws_iam.PolicyDocument(statements=[
                        aws_iam.PolicyStatement(
                            effect=aws_iam.Effect.ALLOW,
                            actions=[
                                "logs:CreateLogStream",
                                "logs:DescribeLogStreams", "logs:PutLogEvents"
                            ],
                            resources=[
                                app_log_group.attr_arn,
                                system_log_group.attr_arn
                            ])
                    ]),
                    policy_name="AllowStreamLogsToCloudWatch"),
                aws_iam.CfnRole.PolicyProperty(
                    policy_document=aws_iam.PolicyDocument(statements=[
                        aws_iam.PolicyStatement(
                            effect=aws_iam.Effect.ALLOW,
                            actions=[
                                "ec2:AssociateAddress", "ec2:DescribeVolumes",
                                "ec2:DescribeTags",
                                "cloudwatch:GetMetricStatistics",
                                "cloudwatch:ListMetrics",
                                "cloudwatch:PutMetricData"
                            ],
                            resources=["*"])
                    ]),
                    policy_name="AllowStreamMetricsToCloudWatch"),
                aws_iam.CfnRole.PolicyProperty(
                    policy_document=aws_iam.PolicyDocument(statements=[
                        aws_iam.PolicyStatement(
                            effect=aws_iam.Effect.ALLOW,
                            actions=["autoscaling:Describe*"],
                            resources=["*"])
                    ]),
                    policy_name="AllowDescribeAutoScaling"),
            ],
            managed_policy_arns=[
                "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
            ])

        # ec2
        jitsi_sg = aws_ec2.CfnSecurityGroup(
            self,
            "JitsiSg",
            group_description="Jitsi security group",
            vpc_id=vpc.id())

        eip = aws_ec2.CfnEIP(self, "Eip", domain="vpc")
        core.Tags.of(eip).add("Name", "{}/Eip".format(core.Aws.STACK_NAME))

        ec2_instance_profile = aws_iam.CfnInstanceProfile(
            self, "JitsiInstanceProfile", roles=[iam_jitsi_instance_role.ref])
        with open("jitsi/jitsi_launch_config_user_data.sh") as f:
            jitsi_launch_config_user_data = f.read()
        ec2_launch_config = aws_autoscaling.CfnLaunchConfiguration(
            self,
            "JitsiLaunchConfig",
            image_id=core.Fn.find_in_map("AWSAMIRegionMap", core.Aws.REGION,
                                         "OEJITSI"),
            instance_type=ec2_instance_type_param.value_as_string,
            iam_instance_profile=ec2_instance_profile.ref,
            security_groups=[jitsi_sg.ref],
            user_data=(core.Fn.base64(
                core.Fn.sub(
                    jitsi_launch_config_user_data, {
                        "JitsiHostname":
                        jitsi_hostname_param.value_as_string,
                        "JitsiPublicIP":
                        eip.ref,
                        "LetsEncryptCertificateEmail":
                        notification_email_param.value_as_string
                    }))))

        # autoscaling
        asg = aws_autoscaling.CfnAutoScalingGroup(
            self,
            "JitsiAsg",
            launch_configuration_name=ec2_launch_config.ref,
            desired_capacity="1",
            max_size="1",
            min_size="1",
            vpc_zone_identifier=vpc.public_subnet_ids())
        asg.cfn_options.creation_policy = core.CfnCreationPolicy(
            resource_signal=core.CfnResourceSignal(count=1, timeout="PT15M"))
        asg.cfn_options.update_policy = core.CfnUpdatePolicy(
            auto_scaling_rolling_update=core.CfnAutoScalingRollingUpdate(
                max_batch_size=1,
                min_instances_in_service=0,
                pause_time="PT15M",
                wait_on_resource_signals=True))
        core.Tags.of(asg).add("Name",
                              "{}/JitsiAsg".format(core.Aws.STACK_NAME))

        jitsi_http_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiHttpSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=80,
            group_id=jitsi_sg.ref,
            ip_protocol="tcp",
            to_port=80)
        jitsi_https_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiHttpsSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=443,
            group_id=jitsi_sg.ref,
            ip_protocol="tcp",
            to_port=443)
        jitsi_fallback_network_audio_video_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiFallbackNetworkAudioVideoSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=4443,
            group_id=jitsi_sg.ref,
            ip_protocol="tcp",
            to_port=4443)
        jitsi_general_network_audio_video_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiGeneralNetworkAudioVideoSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=10000,
            group_id=jitsi_sg.ref,
            ip_protocol="udp",
            to_port=10000)

        # route 53
        record_set = aws_route53.CfnRecordSet(
            self,
            "RecordSet",
            hosted_zone_name=
            f"{route_53_hosted_zone_name_param.value_as_string}.",
            name=jitsi_hostname_param.value_as_string,
            resource_records=[eip.ref],
            type="A")
        # https://github.com/aws/aws-cdk/issues/8431
        record_set.add_property_override("TTL", 60)

        # AWS::CloudFormation::Interface
        self.template_options.metadata = {
            "OE::Patterns::TemplateVersion": template_version,
            "AWS::CloudFormation::Interface": {
                "ParameterGroups": [{
                    "Label": {
                        "default": "Infrastructure Config"
                    },
                    "Parameters": [
                        jitsi_hostname_param.logical_id,
                        route_53_hosted_zone_name_param.logical_id,
                        cidr_block_param.logical_id,
                        ec2_instance_type_param.logical_id,
                        notification_email_param.logical_id
                    ]
                }, {
                    "Label": {
                        "default": "Jitsi Config"
                    },
                    "Parameters": [
                        jitsi_interface_app_name_param.logical_id,
                        jitsi_interface_default_remote_display_name_param.
                        logical_id,
                        jitsi_interface_native_app_name_param.logical_id,
                        jitsi_interface_show_brand_watermark_param.logical_id,
                        jitsi_interface_show_watermark_for_guests_param.
                        logical_id,
                        jitsi_interface_brand_watermark_param.logical_id,
                        jitsi_interface_brand_watermark_link_param.logical_id,
                        jitsi_interface_watermark_param.logical_id,
                        jitsi_interface_watermark_link_param.logical_id,
                    ]
                }, *vpc.metadata_parameter_group()],
                "ParameterLabels": {
                    cidr_block_param.logical_id: {
                        "default": "Ingress CIDR Block"
                    },
                    ec2_instance_type_param.logical_id: {
                        "default": "EC2 instance type"
                    },
                    jitsi_hostname_param.logical_id: {
                        "default": "Jitsi Hostname"
                    },
                    jitsi_interface_app_name_param.logical_id: {
                        "default": "Jitsi Interface App Name"
                    },
                    jitsi_interface_default_remote_display_name_param.logical_id:
                    {
                        "default":
                        "Jitsi Interface Default Remote Display Name"
                    },
                    jitsi_interface_native_app_name_param.logical_id: {
                        "default": "Jitsi Interface Native App Name"
                    },
                    jitsi_interface_show_brand_watermark_param.logical_id: {
                        "default": "Jitsi Interface Show Watermark"
                    },
                    jitsi_interface_show_watermark_for_guests_param.logical_id:
                    {
                        "default": "Jitsi Interface Show Watermark For Guests"
                    },
                    jitsi_interface_brand_watermark_param.logical_id: {
                        "default": "Jitsi Interface Watermark"
                    },
                    jitsi_interface_brand_watermark_link_param.logical_id: {
                        "default": "Jitsi Interface Watermark Link"
                    },
                    jitsi_interface_watermark_param.logical_id: {
                        "default": "Jitsi Interface Watermark"
                    },
                    jitsi_interface_watermark_link_param.logical_id: {
                        "default": "Jitsi Interface Watermark Link"
                    },
                    notification_email_param.logical_id: {
                        "default": "Notification Email"
                    },
                    route_53_hosted_zone_name_param.logical_id: {
                        "default": "AWS Route 53 Hosted Zone Name"
                    },
                    **vpc.metadata_parameter_labels()
                }
            }
        }

        #
        # OUTPUTS
        #

        eip_output = core.CfnOutput(
            self,
            "EipOutput",
            description=
            "The Elastic IP address dynamically mapped to the autoscaling group instance.",
            value=eip.ref)
        endpoint_output = core.CfnOutput(
            self,
            "JitsiUrl",
            description="The URL for the Jitsi instance.",
            value=core.Fn.join(
                "", ["https://", jitsi_hostname_param.value_as_string]))
    def _add_code_build_docker_image_builder_project(self):
        timestamp = f"{datetime.utcnow().strftime('%Y%m%d%H%M')}"
        log_group_name = (
            f"{CW_LOG_GROUP_NAME_PREFIX}codebuild/{self.stack_name}-CodeBuildDockerImageBuilderProject-{timestamp}"
        )

        log_group = logs.CfnLogGroup(
            self.stack_scope,
            "CodeBuildLogGroup",
            log_group_name=log_group_name,
            retention_in_days=get_cloud_watch_logs_retention_days(self.config),
        )
        log_group.cfn_options.deletion_policy = get_log_group_deletion_policy(
            self.config)

        return codebuild.CfnProject(
            self.stack_scope,
            "CodeBuildDockerImageBuilderProj",
            artifacts=codebuild.CfnProject.ArtifactsProperty(
                type="NO_ARTIFACTS"),
            environment=codebuild.CfnProject.EnvironmentProperty(
                compute_type="BUILD_GENERAL1_LARGE"
                if self._condition_use_arm_code_build_image() else
                "BUILD_GENERAL1_SMALL",
                environment_variables=[
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="AWS_REGION",
                        value=self._stack_region,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="AWS_ACCOUNT_ID",
                        value=self._stack_account,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="IMAGE_REPO_NAME",
                        value=self._docker_images_repo.ref,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="IMAGE",
                        value=self.config.image.os,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="NOTIFICATION_URL",
                        value=self._docker_build_wait_condition_handle.ref,
                    ),
                ],
                image="aws/codebuild/amazonlinux2-aarch64-standard:1.0"
                if self._condition_use_arm_code_build_image() else
                "aws/codebuild/amazonlinux2-x86_64-standard:3.0",
                type="ARM_CONTAINER"
                if self._condition_use_arm_code_build_image() else
                "LINUX_CONTAINER",
                privileged_mode=True,
            ),
            name=f"pcluster-{self.stack_name}-build-docker-images-project",
            service_role=self._code_build_role.attr_arn,
            source=codebuild.CfnProject.SourceProperty(
                location=f"{self.bucket.name}/{self.bucket.artifact_directory}"
                "/custom_resources/scheduler_resources.zip",
                type="S3",
            ),
            logs_config=codebuild.CfnProject.LogsConfigProperty(
                cloud_watch_logs=codebuild.CfnProject.
                CloudWatchLogsConfigProperty(group_name=log_group_name,
                                             status="ENABLED")),
        )
    def __init__(self, scope: core.Construct, id: str, config: Config, **kwargs):
        super().__init__(scope, id, **kwargs)

        logs.CfnLogGroup(
            self,
            id=config.log_group_id(),
            retention_in_days=config.log_retention_days()
        )
        for role, policies in config.roles().items():
            ServiceRole(
                self,
                id=role,
                policies=policies,
                service='ecs-tasks.amazonaws.com',
                whitelisted_actions=config.whitelisted_actions()
            )
        TaskDefinition(
            self,
            id='TaskDefinition',
            containers=config.containers(),
            cpu=config.task_cpu(),
            memory=config.task_memory(),
            log_group_id=config.log_group_id(),
            execution_role_id=config.execution_role_id(),
            task_role_id=config.task_role_id(),
            overrides=config.task_overrides()
        )
        TargetGroup(
            self,
            id='TargetGroup',
            health_check_path=config.service_health_check_path(),
            port=config.service_port(),
            protocol=str.upper(config.service_health_check_protocol()),
            vpc_id=config.vpc_id(),
            overrides=config.target_group_overrides()
        )
        Service(
            self,
            id='Service',
            cluster_name=config.cluster_stack_name(),
            desired_count=config.service_desired_count(),
            service_name=config.service_name(),
            service_port=config.service_port(),
            security_group_import_name=config.shared_service_security_group_export_name(),
            subnet_ids=config.private_subnet_ids(),
            target_group_id='TargetGroup',
            task_def_id='TaskDefinition',
            overrides=config.service_overrides()
        )
        ListenerRule(
            self,
            id='ListenerRule',
            listener_import_name=config.shared_lb_listener_export_name(),
            paths=config.service_paths(),
            priority=config.service_priority(),
            target_group_id='TargetGroup'
        )
        RecordSet(
            self,
            id='RecordSet',
            hosted_zone_name=config.hosted_zone_name(),
            dns_name_import_name=config.shared_lb_dns_name_export_name(),
            hosted_zone_id_import_name=config.shared_lb_hosted_zone_id_export_name()
        )

        for key, value in config.tags().items():
            core.Tag.add(self, key, value)

        self.node.apply_aspect(UseOriginalConstructID())