示例#1
0
def convert_yaml_node_to_troposphere(node):
    if node.tag == '!Sub':
        if type(node.value) == type(str()):
            # ScalarNode - single argument only
            return troposphere.Sub(node.value)
        else:
            values = {}
            for map_node in node.value[1:]:
                if map_node.tag != yaml_map_tag:
                    raise TroposphereConversionError(
                        "Substitue variables for !Sub must be mappings.")
                values[map_node.value[0][0].value] = map_node.value[0][1].value
            return troposphere.Sub(node.value[0].value, **values)

    elif node.tag == '!Ref':
        return troposphere.Ref(node.value)
    elif node.tag == '!Join':
        delimiter = node.value[0].value
        values = []
        for node in node.value[1].value:
            values.append(convert_yaml_node_to_troposphere(node))
        return troposphere.Join(delimiter, values)

    elif node.tag == yaml_str_tag:
        return node.value
    else:
        raise TroposphereConversionError(
            "Unknown YAML to convert to Troposphere")
示例#2
0
    def register_type_project_template(cls, project, template):
        """Registers into the project stack a S3 bucket where all lambdas
        code will be stored, as well as an output so any subsequent template
        can have a reference to this resource."""

        bucket_name = troposphere.Join(
            "-",
            [
                utils.validate_code_bucket(project.settings['code-bucket']),
                troposphere.Ref(troposphere.AWS_REGION),
                troposphere.Ref('Stage')
            ]
        )
        code_bucket = s3.Bucket(
            "CodeBucket",
            BucketName=bucket_name,
            AccessControl=s3.Private,
            VersioningConfiguration=s3.VersioningConfiguration(
                Status='Enabled'
            )
        )
        template.add_resource(code_bucket)
        template.add_output([
            troposphere.Output(
                "CodeBucket",
                Description="CodeBucket name",
                Value=bucket_name,
            )
        ])
示例#3
0
 def queue_policy(self):
     return ts.sqs.QueuePolicy(
         self._get_logical_id('QueuePolicy'),
         Queues=[ts.Ref(self.queue)],
         PolicyDocument={
             'Version':
             '2012-10-17',
             'Statement': [{
                 'Sid': 'allow-sns-notifications',
                 'Effect': 'Allow',
                 'Principal': {
                     'AWS': '*',
                 },
                 'Action': ['sqs:SendMessage'],
                 'Resource': '*',
                 'Condition': {
                     # todo: allow all topics?
                     # 'ArnEquals': {'aws:SourceArn': self.config['SourceTopics']}
                     'ArnLike': {
                         'aws:SourceArn':
                         ts.Join(':', [
                             'arn:aws:sns',
                             ts.Ref('AWS::Region'),
                             ts.Ref('AWS::AccountId'),
                             '*',
                         ])
                     },
                 },
             }],
         },
     )
示例#4
0
 def create_lambda_invoke_properties(self, stage, action, info):
     function_arn_param = self.create_cfn_parameter(
         param_type='String',
         name=self.create_cfn_logical_id('Lambda' + stage.name +
                                         action.name),
         description='The name of the Lambda for stage {} and action {}'.
         format(stage.name, action.name),
         value=action.target_lambda + '.arn',
     )
     user_parameters_param = self.create_cfn_parameter(
         param_type='String',
         name=self.create_cfn_logical_id('UserParameters' + stage.name +
                                         action.name),
         description='The UserParameters for stage {} and action {}'.format(
             stage.name, action.name),
         value=action.user_parameters,
     )
     lambda_function_name = troposphere.Join('', [
         troposphere.Select(
             6, troposphere.Split(':', troposphere.Ref(function_arn_param)))
     ])
     return {
         'Configuration': {
             'FunctionName': lambda_function_name,
             'UserParameters': troposphere.Ref(user_parameters_param),
         },
     }
示例#5
0
 def ec2_user_data(self):
     return ts.Base64(
         ts.Join(
             '',
             [
                 # -x print commands as they're executed
                 # -e exit on error
                 '#!/bin/bash -xe',
                 '\n',
                 # todo: figure out what these commands do
                 'yum install -y aws-cfn-bootstrap',
                 '\n',
                 '/opt/aws/bin/cfn-init -v ',
                 '    --stack    ',
                 ts.Ref('AWS::StackName'),
                 '    --resource ',
                 self.launch_config_logical_id,
                 '    --region   ',
                 ts.Ref('AWS::Region'),
                 '\n',
                 '/opt/aws/bin/cfn-signal -e $? ',
                 '     --stack   ',
                 ts.Ref('AWS::StackName'),
                 '    --resource ',
                 self.auto_scaling_group_logical_id,
                 '    --region   ',
                 ts.Ref('AWS::Region'),
                 '\n',
             ]))
示例#6
0
文件: s3.py 项目: revolunet/gordon
    def register_resources_template(self, template):

        extra = defaultdict(list)
        for notification_id, notification in six.iteritems(
                self._notifications):
            notification.register_destination_publish_permission(template)

            extra[notification.api_property].append(
                NotificationConfiguration(
                    Id=troposphere.Join('-', ['gordon', notification.id]),
                    DestinationArn=notification.get_destination_arn(),
                    Events=[e for e, _, _ in notification.events],
                    KeyFilters=[
                        KeyFilter(Name=name, Value=value)
                        for name, value in notification.filters
                    ]))

        bucket_notification_configuration_lambda = 'lambda:contrib_s3:bucket_notification_configuration:current'
        template.add_resource(
            S3BucketNotificationConfiguration.create_with(
                utils.valid_cloudformation_name(self.name),
                DependsOn=[
                    self.project.reference(
                        bucket_notification_configuration_lambda)
                ],
                lambda_arn=troposphere.Ref(
                    self.project.reference(
                        bucket_notification_configuration_lambda)),
                Bucket=self.get_bucket_name(),
                **dict([[k, v] for k, v in six.iteritems(extra) if v])))
示例#7
0
 def profile_role_statements(self):
     return collections.ChainMap(
         {
             # allow pulling s3 config
             'access-secrets-bucket':
             awacs.aws.Statement(
                 Effect='Allow',
                 Action=[awacs.s3.Action('*')],
                 Resource=[
                     ts.Join('', [
                         'arn:aws:s3:::',
                         ts.Ref(self.secrets_bucket), '*'
                     ])
                 ],
             ),
             # allow decrypting s3 secrets
             'use-secrets-kms-key':
             awacs.aws.Statement(
                 Effect='Allow',
                 Action=[awacs.kms.Action('*')],
                 Resource=[self.secrets_key_arn],
             ),
         },
         super().profile_role_statements,
     )
示例#8
0
    def get_default_bucket_policy_statements(self, pipeline_bucket):
        bucket_policy_statements = [
            awacs.aws.Statement(
                Effect=awacs.aws.Allow,
                Action=[
                    awacs.s3.ListBucket,
                    awacs.s3.GetBucketVersioning,
                ],
                Resource=[
                    troposphere.Join('', [
                        awacs.s3.ARN(),
                        pipeline_bucket,
                    ]),
                ],
            ),
            awacs.aws.Statement(
                Effect=awacs.aws.Allow,
                Action=[
                    awacs.s3.HeadBucket,
                ],
                Resource=[
                    '*'
                ]
            ),
            awacs.aws.Statement(
                Effect=awacs.aws.Allow,
                Action=[
                    awacs.s3.GetObject,
                    awacs.s3.GetObjectVersion,
                    awacs.s3.PutObject,
                    awacs.s3.ListObjects,
                    awacs.s3.ListBucketMultipartUploads,
                    awacs.s3.AbortMultipartUpload,
                    awacs.s3.ListMultipartUploadParts,
                    awacs.aws.Action("s3", "Get*"),
                ],
                Resource=[
                    troposphere.Join('', [
                        awacs.s3.ARN(),
                        pipeline_bucket,
                        '/*'
                    ]),
                ],
            )
        ]

        return bucket_policy_statements
    def __init__(self, paco_ctx, account_ctx, aws_region, stack_group,
                 stack_tags, zone_config, config_ref):
        super().__init__(paco_ctx,
                         account_ctx,
                         aws_region,
                         enabled=zone_config.is_enabled(),
                         config_ref=config_ref,
                         iam_capabilities=["CAPABILITY_NAMED_IAM"],
                         stack_group=stack_group,
                         stack_tags=stack_tags)
        self.set_aws_name('HostedZone', zone_config.name)

        self.init_template('Route53 Hosted Zone: ' + zone_config.domain_name)

        self.paco_ctx.log_action_col("Init", "Route53", "Hosted Zone",
                                     "{}".format(zone_config.domain_name))

        if zone_config.external_resource != None and zone_config.external_resource.is_enabled(
        ):
            hosted_zone_id_output_value = zone_config.external_resource.hosted_zone_id
            nameservers_output_value = ','.join(
                zone_config.external_resource.nameservers)
        else:
            hosted_zone_res = troposphere.route53.HostedZone(
                title='HostedZone',
                template=self.template,
                Name=zone_config.domain_name)
            hosted_zone_id_output_value = troposphere.Ref(hosted_zone_res)
            nameservers_output_value = troposphere.Join(
                ',', troposphere.GetAtt(hosted_zone_res, 'NameServers'))

        self.create_output(title='HostedZoneId',
                           value=hosted_zone_id_output_value,
                           ref=config_ref + '.id')
        self.create_output(title='HostedZoneNameServers',
                           value=nameservers_output_value,
                           ref=config_ref + '.name_servers')

        if len(zone_config.record_sets) > 0:
            record_set_list = []
            for record_set_config in zone_config.record_sets:
                record_set_res = troposphere.route53.RecordSet(
                    Name=record_set_config.record_name,
                    Type=record_set_config.type,
                    TTL=record_set_config.ttl,
                    ResourceRecords=record_set_config.resource_records)
                record_set_list.append(record_set_res)

            group_res = troposphere.route53.RecordSetGroup(
                title='RecordSetGroup',
                template=self.template,
                HostedZoneId=troposphere.Ref(hosted_zone_res),
                RecordSets=record_set_list)
            group_res.DependsOn = hosted_zone_res

        self.set_template()
示例#10
0
    def __init__(self, stack, paco_ctx, awslambda):
        super().__init__(stack, paco_ctx)
        self.set_aws_name('ApiGatewayLamdaPermission', self.resource_group_name, self.resource_name)
        self.init_template('Cross-account Api Gateway Lambda Permission')
        apigateway = self.resource

        api_gateway_id_param = self.create_cfn_parameter(
            name=self.create_cfn_logical_id('ApiGatewayRestApiId'),
            param_type='String',
            description='API Gateway Rest API Id',
            value=apigateway.paco_ref + '.id',
        )
        lambda_arn_param = self.create_cfn_parameter(
            name=self.create_cfn_logical_id('LambdaArn'),
            param_type='String',
            description='Lambda Arn',
            value=awslambda.paco_ref + '.arn',
        )

        # Lambda Permission for cross-account API Gateway invocation
        for method in apigateway.methods.values():
            if method.integration != None and method.integration.integration_lambda != None:
                if awslambda.paco_ref == method.integration.integration_lambda:
                    if apigateway.get_account().name != awslambda.get_account().name:
                        # Grant Cross-Account API Gateway permission
                        path_part = ''
                        if method.resource_name:
                            name_parts = method.resource_name.split('.')
                            resource = method.get_resource()
                            if len(name_parts) > 1:
                                # child resource
                                last_resource = resource
                                while schemas.IApiGatewayResource.providedBy(resource):
                                    last_resource = resource
                                    resource = resource.__parent__.__parent__
                                path_part = last_resource.path_part + '/*' # add /* to match all child resource
                            else:
                                # parent resource
                                path_part = resource.path_part
                        lambda_permission_resource = troposphere.awslambda.Permission(
                            title='ApiGatewayRestApiMethod' + md5sum(str_data=method.paco_ref),
                            Action="lambda:InvokeFunction",
                            FunctionName=troposphere.Ref(lambda_arn_param),
                            Principal='apigateway.amazonaws.com',
                            SourceArn=troposphere.Join('', [
                                "arn:aws:execute-api:",
                                awslambda.region_name, # lambda region
                                ":",
                                apigateway.get_account().account_id, # account id
                                ":",
                                troposphere.Ref(api_gateway_id_param),
                                f"/*/{method.http_method}/{path_part}",
                            ])
                        )
                        self.template.add_resource(lambda_permission_resource)
示例#11
0
    def create_template(self):
        t = self.template
        t.add_description("Acceptance Tests for cumulus scaling groups")

        the_chain = chain.Chain()

        application_port = "8000"

        the_chain.add(
            launch_config.LaunchConfig(prefix='websitesimple',
                                       vpc_id=Ref('VpcId'),
                                       meta_data=self.get_metadata(),
                                       bucket_name=self.context.bucket_name))

        the_chain.add(
            ingress_rule.IngressRule(port_to_open="22", cidr="10.0.0.0/8"))

        the_chain.add(
            ingress_rule.IngressRule(port_to_open=application_port,
                                     cidr="10.0.0.0/8"))

        the_chain.add(
            block_device_data.BlockDeviceData(
                ec2.BlockDeviceMapping(
                    DeviceName="/dev/xvda",
                    Ebs=ec2.EBSBlockDevice(VolumeSize="40"))))

        the_chain.add(
            target_group.TargetGroup(port=application_port,
                                     vpc_id=Ref("VpcId")))

        the_chain.add(scaling_group.ScalingGroup())

        the_chain.add(
            dns.Dns(base_domain=Ref("BaseDomain"),
                    hosted_zone_id=Ref("AlbCanonicalHostedZoneID"),
                    target=Ref("AlbDNSName"),
                    dns_name=troposphere.Join('', [
                        self.context.namespace,
                        "-websitesimple",
                    ])))

        the_chain.add(alb_port.AlbPort(port_to_open=application_port, ))

        the_chain.add(
            listener_rule.ListenerRule(base_domain_name=Ref("BaseDomain"),
                                       alb_listener_rule=Ref("IAlbListener"),
                                       path_pattern="/*",
                                       priority="2"))

        chain_context = chaincontext.ChainContext(template=t, )

        the_chain.run(chain_context)
示例#12
0
def to_userdata(userdata_str, base64_encode=True):
    splitted = re.split('(%s)' % ('|'.join(map(lambda e: e.split_exp, SUPPORTED_ELEMENTS))), userdata_str)

    def check(value):
        for e in SUPPORTED_ELEMENTS:
            try:
                return e.do(value)
            except NotMatchError:
                ...
        return value

    userdata = troposphere.Join('', list(map(check, splitted)))
    return troposphere.Base64(userdata) if base64_encode else userdata
示例#13
0
 def get_integration_uri(self, resource):
     integration_type = self._get_integration_type(resource)
     if integration_type == LAMBDA_INTEGRATION:
         return troposphere.Join('', [
             'arn:aws:apigateway:',
             troposphere.Ref(troposphere.AWS_REGION),
             ':lambda:path/2015-03-31/functions/',
             troposphere.Ref(
                 self.get_function_name(resource)), '/invocations'
         ])
     elif integration_type == HTTP_INTEGRATION:
         return resource['integration']['uri']
     elif integration_type == MOCK_INTEGRATION:
         return troposphere.Ref(troposphere.AWS_NO_VALUE)
示例#14
0
文件: s3.py 项目: revolunet/gordon
    def get_destination_arn(self):
        destination = self.settings['topic']
        region = troposphere.Ref(troposphere.AWS_REGION)

        if isinstance(destination, six.string_types):
            if destination.startswith('arn:aws:'):
                return destination
            account = troposphere.Ref(troposphere.AWS_ACCOUNT_ID)
        elif isinstance(destination, dict):
            account = destination['account_id']
            destination = destination['name']
        else:
            return destination

        return troposphere.Join(":",
                                ["arn:aws:sns", region, account, destination])
示例#15
0
文件: s3.py 项目: revolunet/gordon
    def get_destination_url(self):
        destination = self.settings['queue']
        region = troposphere.Ref(troposphere.AWS_REGION)

        if isinstance(destination, six.string_types):
            account = troposphere.Ref(troposphere.AWS_ACCOUNT_ID)
        elif isinstance(destination, dict):
            account = destination['account_id']
            destination = destination['name']
        else:
            return destination

        return troposphere.Join("", [
            "https://sqs.", region, ".amazonaws.com/", account, "/",
            destination
        ])
示例#16
0
def create_record(awsclient,
                  name_prefix,
                  instance_reference,
                  type="A",
                  host_zone_name=None):
    """
    Builds route53 record entries enabling DNS names for services
    Note: gcdt.route53 create_record(awsclient, ...)
    is used in dataplatform cloudformation.py templates!

    :param name_prefix: The sub domain prefix to use
    :param instance_reference: The EC2 troposphere reference which's private IP should be linked to
    :param type: The type of the record  A or CNAME (default: A)
    :param host_zone_name: The host zone name to use (like preprod.ds.glomex.cloud. - DO NOT FORGET THE DOT!)
    :return: RecordSetType
    """

    # Only fetch the host zone from the COPS stack if nessary
    if host_zone_name is None:
        host_zone_name = _retrieve_stack_host_zone_name(awsclient)

    if not (type == "A" or type == "CNAME"):
        raise Exception("Record set type is not supported!")

    name_of_record = name_prefix \
                         .replace('.', '') \
                         .replace('-', '') \
                         .title() + "HostRecord"

    # Reference EC2 instance automatically to their private IP
    if isinstance(instance_reference, Instance):
        resource_record = troposphere.GetAtt(instance_reference, "PrivateIp")
    else:
        resource_record = instance_reference

    return RecordSetType(
        name_of_record,
        HostedZoneName=host_zone_name,
        Name=troposphere.Join("", [
            name_prefix + ".",
            host_zone_name,
        ]),
        Type=type,
        TTL=TTL_DEFAULT,
        ResourceRecords=[resource_record],
    )
def get_template(
    puppet_version,
    all_regions,
    source,
    is_caching_enabled,
    is_manual_approvals: bool,
    scm_skip_creation_of_repo: bool,
    should_validate: bool,
) -> t.Template:
    is_codecommit = source.get("Provider", "").lower() == "codecommit"
    is_github = source.get("Provider", "").lower() == "github"
    is_codestarsourceconnection = (source.get(
        "Provider", "").lower() == "codestarsourceconnection")
    is_custom = (source.get("Provider", "").lower() == "custom")
    is_s3 = source.get("Provider", "").lower() == "s3"
    description = f"""Bootstrap template used to bring up the main ServiceCatalog-Puppet AWS CodePipeline with dependencies
{{"version": "{puppet_version}", "framework": "servicecatalog-puppet", "role": "bootstrap-master"}}"""

    template = t.Template(Description=description)

    version_parameter = template.add_parameter(
        t.Parameter("Version", Default=puppet_version, Type="String"))
    org_iam_role_arn_parameter = template.add_parameter(
        t.Parameter("OrgIamRoleArn", Default="None", Type="String"))
    with_manual_approvals_parameter = template.add_parameter(
        t.Parameter(
            "WithManualApprovals",
            Type="String",
            AllowedValues=["Yes", "No"],
            Default="No",
        ))
    puppet_code_pipeline_role_permission_boundary_parameter = template.add_parameter(
        t.Parameter(
            "PuppetCodePipelineRolePermissionBoundary",
            Type="String",
            Description=
            "IAM Permission Boundary to apply to the PuppetCodePipelineRole",
            Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data,
        ))
    source_role_permissions_boundary_parameter = template.add_parameter(
        t.Parameter(
            "SourceRolePermissionsBoundary",
            Type="String",
            Description="IAM Permission Boundary to apply to the SourceRole",
            Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data,
        ))
    puppet_generate_role_permission_boundary_parameter = template.add_parameter(
        t.Parameter(
            "PuppetGenerateRolePermissionBoundary",
            Type="String",
            Description=
            "IAM Permission Boundary to apply to the PuppetGenerateRole",
            Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data,
        ))
    puppet_deploy_role_permission_boundary_parameter = template.add_parameter(
        t.Parameter(
            "PuppetDeployRolePermissionBoundary",
            Type="String",
            Description=
            "IAM Permission Boundary to apply to the PuppetDeployRole",
            Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data,
        ))
    puppet_provisioning_role_permissions_boundary_parameter = template.add_parameter(
        t.Parameter(
            "PuppetProvisioningRolePermissionsBoundary",
            Type="String",
            Description=
            "IAM Permission Boundary to apply to the PuppetProvisioningRole",
            Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data,
        ))
    cloud_formation_deploy_role_permissions_boundary_parameter = template.add_parameter(
        t.Parameter(
            "CloudFormationDeployRolePermissionsBoundary",
            Type="String",
            Description=
            "IAM Permission Boundary to apply to the CloudFormationDeployRole",
            Default=awscs_iam.ARN(resource="policy/AdministratorAccess").data,
        ))
    deploy_environment_compute_type_parameter = template.add_parameter(
        t.Parameter(
            "DeployEnvironmentComputeType",
            Type="String",
            Description="The AWS CodeBuild Environment Compute Type",
            Default="BUILD_GENERAL1_SMALL",
        ))
    spoke_deploy_environment_compute_type_parameter = template.add_parameter(
        t.Parameter(
            "SpokeDeployEnvironmentComputeType",
            Type="String",
            Description=
            "The AWS CodeBuild Environment Compute Type for spoke execution mode",
            Default="BUILD_GENERAL1_SMALL",
        ))
    deploy_num_workers_parameter = template.add_parameter(
        t.Parameter(
            "DeployNumWorkers",
            Type="Number",
            Description=
            "Number of workers that should be used when running a deploy",
            Default=10,
        ))
    puppet_role_name_parameter = template.add_parameter(
        t.Parameter("PuppetRoleName", Type="String", Default="PuppetRole"))
    puppet_role_path_template_parameter = template.add_parameter(
        t.Parameter("PuppetRolePath",
                    Type="String",
                    Default="/servicecatalog-puppet/"))

    template.add_condition(
        "ShouldUseOrgs",
        t.Not(t.Equals(t.Ref(org_iam_role_arn_parameter), "None")))
    template.add_condition(
        "HasManualApprovals",
        t.Equals(t.Ref(with_manual_approvals_parameter), "Yes"))

    template.add_resource(
        s3.Bucket(
            "StacksRepository",
            BucketName=t.Sub("sc-puppet-stacks-repository-${AWS::AccountId}"),
            VersioningConfiguration=s3.VersioningConfiguration(
                Status="Enabled"),
            BucketEncryption=s3.BucketEncryption(
                ServerSideEncryptionConfiguration=[
                    s3.ServerSideEncryptionRule(
                        ServerSideEncryptionByDefault=s3.
                        ServerSideEncryptionByDefault(SSEAlgorithm="AES256"))
                ]),
            PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration(
                BlockPublicAcls=True,
                BlockPublicPolicy=True,
                IgnorePublicAcls=True,
                RestrictPublicBuckets=True,
            ),
            Tags=t.Tags({"ServiceCatalogPuppet:Actor": "Framework"}),
        ))

    manual_approvals_param = template.add_resource(
        ssm.Parameter(
            "ManualApprovalsParam",
            Type="String",
            Name="/servicecatalog-puppet/manual-approvals",
            Value=t.Ref(with_manual_approvals_parameter),
        ))
    template.add_resource(
        ssm.Parameter(
            "SpokeDeployEnvParameter",
            Type="String",
            Name=constants.SPOKE_EXECUTION_MODE_DEPLOY_ENV_PARAMETER_NAME,
            Value=t.Ref(spoke_deploy_environment_compute_type_parameter),
        ))
    param = template.add_resource(
        ssm.Parameter(
            "Param",
            Type="String",
            Name="service-catalog-puppet-version",
            Value=t.Ref(version_parameter),
        ))
    partition_parameter = template.add_resource(
        ssm.Parameter(
            "PartitionParameter",
            Type="String",
            Name="/servicecatalog-puppet/partition",
            Value=t.Ref("AWS::Partition"),
        ))
    puppet_role_name_parameter = template.add_resource(
        ssm.Parameter(
            "PuppetRoleNameParameter",
            Type="String",
            Name="/servicecatalog-puppet/puppet-role/name",
            Value=t.Ref(puppet_role_name_parameter),
        ))
    puppet_role_path_parameter = template.add_resource(
        ssm.Parameter(
            "PuppetRolePathParameter",
            Type="String",
            Name="/servicecatalog-puppet/puppet-role/path",
            Value=t.Ref(puppet_role_path_template_parameter),
        ))
    share_accept_function_role = template.add_resource(
        iam.Role(
            "ShareAcceptFunctionRole",
            RoleName="ShareAcceptFunctionRole",
            ManagedPolicyArns=[
                t.Sub(
                    "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
                )
            ],
            Path=t.Ref(puppet_role_path_template_parameter),
            Policies=[
                iam.Policy(
                    PolicyName="ServiceCatalogActions",
                    PolicyDocument={
                        "Version":
                        "2012-10-17",
                        "Statement": [{
                            "Action": ["sts:AssumeRole"],
                            "Resource": {
                                "Fn::Sub":
                                "arn:${AWS::Partition}:iam::*:role${PuppetRolePath}${PuppetRoleName}"
                            },
                            "Effect": "Allow",
                        }],
                    },
                )
            ],
            AssumeRolePolicyDocument={
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Action": ["sts:AssumeRole"],
                    "Effect": "Allow",
                    "Principal": {
                        "Service": ["lambda.amazonaws.com"]
                    },
                }],
            },
        ))

    provisioning_role = template.add_resource(
        iam.Role(
            "ProvisioningRole",
            RoleName="PuppetProvisioningRole",
            AssumeRolePolicyDocument={
                "Version":
                "2012-10-17",
                "Statement": [
                    {
                        "Action": ["sts:AssumeRole"],
                        "Effect": "Allow",
                        "Principal": {
                            "Service": ["codebuild.amazonaws.com"]
                        },
                    },
                    {
                        "Action": ["sts:AssumeRole"],
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": {
                                "Fn::Sub": "${AWS::AccountId}"
                            }
                        },
                    },
                ],
            },
            ManagedPolicyArns=[
                t.Sub(
                    "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
                )
            ],
            PermissionsBoundary=t.Ref(
                puppet_provisioning_role_permissions_boundary_parameter),
            Path=t.Ref(puppet_role_path_template_parameter),
        ))

    cloud_formation_deploy_role = template.add_resource(
        iam.Role(
            "CloudFormationDeployRole",
            RoleName="CloudFormationDeployRole",
            AssumeRolePolicyDocument={
                "Version":
                "2012-10-17",
                "Statement": [
                    {
                        "Action": ["sts:AssumeRole"],
                        "Effect": "Allow",
                        "Principal": {
                            "Service": ["cloudformation.amazonaws.com"]
                        },
                    },
                    {
                        "Action": ["sts:AssumeRole"],
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": {
                                "Fn::Sub": "${AWS::AccountId}"
                            }
                        },
                    },
                ],
            },
            ManagedPolicyArns=[
                t.Sub(
                    "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
                )
            ],
            PermissionsBoundary=t.Ref(
                cloud_formation_deploy_role_permissions_boundary_parameter),
            Path=t.Ref(puppet_role_path_template_parameter),
        ))

    pipeline_role = template.add_resource(
        iam.Role(
            "PipelineRole",
            RoleName="PuppetCodePipelineRole",
            AssumeRolePolicyDocument={
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Action": ["sts:AssumeRole"],
                    "Effect": "Allow",
                    "Principal": {
                        "Service": ["codepipeline.amazonaws.com"]
                    },
                }],
            },
            ManagedPolicyArns=[
                t.Sub(
                    "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
                )
            ],
            PermissionsBoundary=t.Ref(
                puppet_code_pipeline_role_permission_boundary_parameter),
            Path=t.Ref(puppet_role_path_template_parameter),
        ))

    source_role = template.add_resource(
        iam.Role(
            "SourceRole",
            RoleName="PuppetSourceRole",
            AssumeRolePolicyDocument={
                "Version":
                "2012-10-17",
                "Statement": [
                    {
                        "Action": ["sts:AssumeRole"],
                        "Effect": "Allow",
                        "Principal": {
                            "Service": ["codepipeline.amazonaws.com"]
                        },
                    },
                    {
                        "Action": ["sts:AssumeRole"],
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": {
                                "Fn::Sub":
                                "arn:${AWS::Partition}:iam::${AWS::AccountId}:root"
                            }
                        },
                    },
                ],
            },
            ManagedPolicyArns=[
                t.Sub(
                    "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
                )
            ],
            PermissionsBoundary=t.Ref(
                source_role_permissions_boundary_parameter),
            Path=t.Ref(puppet_role_path_template_parameter),
        ))

    dry_run_notification_topic = template.add_resource(
        sns.Topic(
            "DryRunNotificationTopic",
            DisplayName="service-catalog-puppet-dry-run-approvals",
            TopicName="service-catalog-puppet-dry-run-approvals",
            Condition="HasManualApprovals",
        ))

    deploy_role = template.add_resource(
        iam.Role(
            "DeployRole",
            RoleName="PuppetDeployRole",
            AssumeRolePolicyDocument={
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Action": ["sts:AssumeRole"],
                    "Effect": "Allow",
                    "Principal": {
                        "Service": ["codebuild.amazonaws.com"]
                    },
                }],
            },
            ManagedPolicyArns=[
                t.Sub(
                    "arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess"
                )
            ],
            PermissionsBoundary=t.Ref(
                puppet_deploy_role_permission_boundary_parameter),
            Path=t.Ref(puppet_role_path_template_parameter),
        ))

    num_workers_ssm_parameter = template.add_resource(
        ssm.Parameter(
            "NumWorkersSSMParameter",
            Type="String",
            Name="/servicecatalog-puppet/deploy/num-workers",
            Value=t.Sub("${DeployNumWorkers}"),
        ))

    parameterised_source_bucket = template.add_resource(
        s3.Bucket(
            "ParameterisedSourceBucket",
            PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration(
                IgnorePublicAcls=True,
                BlockPublicPolicy=True,
                BlockPublicAcls=True,
                RestrictPublicBuckets=True,
            ),
            BucketEncryption=s3.BucketEncryption(
                ServerSideEncryptionConfiguration=[
                    s3.ServerSideEncryptionRule(
                        ServerSideEncryptionByDefault=s3.
                        ServerSideEncryptionByDefault(SSEAlgorithm="AES256"))
                ]),
            Tags=t.Tags.from_dict(
                **{"ServiceCatalogPuppet:Actor": "Framework"}),
            BucketName=t.Sub("sc-puppet-parameterised-runs-${AWS::AccountId}"),
            VersioningConfiguration=s3.VersioningConfiguration(
                Status="Enabled"),
        ))

    source_stage = codepipeline.Stages(
        Name="Source",
        Actions=[
            codepipeline.Actions(
                RunOrder=1,
                RoleArn=t.GetAtt("SourceRole", "Arn"),
                ActionTypeId=codepipeline.ActionTypeId(
                    Category="Source",
                    Owner="AWS",
                    Version="1",
                    Provider="S3",
                ),
                OutputArtifacts=[
                    codepipeline.OutputArtifacts(Name="ParameterisedSource")
                ],
                Configuration={
                    "S3Bucket": t.Ref(parameterised_source_bucket),
                    "S3ObjectKey": "parameters.zip",
                    "PollForSourceChanges": True,
                },
                Name="ParameterisedSource",
            )
        ],
    )

    install_spec = {
        "runtime-versions":
        dict(python="3.7"),
        "commands": [
            f"pip install {puppet_version}" if "http" in puppet_version else
            f"pip install aws-service-catalog-puppet=={puppet_version}",
        ],
    }

    deploy_env_vars = [
        {
            "Type": "PLAINTEXT",
            "Name": "PUPPET_ACCOUNT_ID",
            "Value": t.Ref("AWS::AccountId"),
        },
        {
            "Type": "PLAINTEXT",
            "Name": "PUPPET_REGION",
            "Value": t.Ref("AWS::Region"),
        },
        {
            "Type": "PARAMETER_STORE",
            "Name": "PARTITION",
            "Value": t.Ref(partition_parameter),
        },
        {
            "Type": "PARAMETER_STORE",
            "Name": "PUPPET_ROLE_NAME",
            "Value": t.Ref(puppet_role_name_parameter),
        },
        {
            "Type": "PARAMETER_STORE",
            "Name": "PUPPET_ROLE_PATH",
            "Value": t.Ref(puppet_role_path_parameter),
        },
    ]

    if is_codecommit:
        template.add_resource(
            codecommit.Repository(
                "CodeRepo",
                RepositoryName=source.get("Configuration").get(
                    "RepositoryName"),
                RepositoryDescription=
                "Repo to store the servicecatalog puppet solution",
                DeletionPolicy="Retain",
            ))

        source_stage.Actions.append(
            codepipeline.Actions(
                RunOrder=1,
                RoleArn=t.GetAtt("SourceRole", "Arn"),
                ActionTypeId=codepipeline.ActionTypeId(
                    Category="Source",
                    Owner="AWS",
                    Version="1",
                    Provider="CodeCommit",
                ),
                OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")],
                Configuration={
                    "RepositoryName":
                    source.get("Configuration").get("RepositoryName"),
                    "BranchName":
                    source.get("Configuration").get("BranchName"),
                    "PollForSourceChanges":
                    source.get("Configuration").get("PollForSourceChanges",
                                                    True),
                },
                Name="Source",
            ))

    if is_github:
        source_stage.Actions.append(
            codepipeline.Actions(
                RunOrder=1,
                ActionTypeId=codepipeline.ActionTypeId(
                    Category="Source",
                    Owner="ThirdParty",
                    Version="1",
                    Provider="GitHub",
                ),
                OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")],
                Configuration={
                    "Owner":
                    source.get("Configuration").get("Owner"),
                    "Repo":
                    source.get("Configuration").get("Repo"),
                    "Branch":
                    source.get("Configuration").get("Branch"),
                    "OAuthToken":
                    t.Join(
                        "",
                        [
                            "{{resolve:secretsmanager:",
                            source.get("Configuration").get(
                                "SecretsManagerSecret"),
                            ":SecretString:OAuthToken}}",
                        ],
                    ),
                    "PollForSourceChanges":
                    source.get("Configuration").get("PollForSourceChanges"),
                },
                Name="Source",
            ))

    if is_custom:
        source_stage.Actions.append(
            codepipeline.Actions(
                RunOrder=1,
                ActionTypeId=codepipeline.ActionTypeId(
                    Category="Source",
                    Owner="Custom",
                    Version=source.get("Configuration").get(
                        "CustomActionTypeVersion"),
                    Provider=source.get("Configuration").get(
                        "CustomActionTypeProvider"),
                ),
                OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")],
                Configuration={
                    "GitUrl": source.get("Configuration").get("GitUrl"),
                    "Branch": source.get("Configuration").get("Branch"),
                    "PipelineName": t.Sub("${AWS::StackName}-pipeline"),
                },
                Name="Source",
            ))
        webhook = codepipeline.Webhook(
            "Webhook",
            Authentication="IP",
            TargetAction="Source",
            AuthenticationConfiguration=codepipeline.WebhookAuthConfiguration(
                AllowedIPRange=source.get("Configuration").get(
                    "GitWebHookIpAddress")),
            Filters=[
                codepipeline.WebhookFilterRule(
                    JsonPath="$.changes[0].ref.id",
                    MatchEquals="refs/heads/{Branch}")
            ],
            TargetPipelineVersion=1,
            TargetPipeline=t.Sub("${AWS::StackName}-pipeline"),
        )
        template.add_resource(webhook)
        values_for_sub = {
            "GitUrl": source.get("Configuration").get("GitUrl"),
            "WebhookUrl": t.GetAtt(webhook, "Url"),
        }
        output_to_add = t.Output("WebhookUrl")
        output_to_add.Value = t.Sub("${GitUrl}||${WebhookUrl}",
                                    **values_for_sub)
        output_to_add.Export = t.Export(t.Sub("${AWS::StackName}-pipeline"))
        template.add_output(output_to_add)

    if is_codestarsourceconnection:
        source_stage.Actions.append(
            codepipeline.Actions(
                RunOrder=1,
                RoleArn=t.GetAtt("SourceRole", "Arn"),
                ActionTypeId=codepipeline.ActionTypeId(
                    Category="Source",
                    Owner="AWS",
                    Version="1",
                    Provider="CodeStarSourceConnection",
                ),
                OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")],
                Configuration={
                    "ConnectionArn":
                    source.get("Configuration").get("ConnectionArn"),
                    "FullRepositoryId":
                    source.get("Configuration").get("FullRepositoryId"),
                    "BranchName":
                    source.get("Configuration").get("BranchName"),
                    "OutputArtifactFormat":
                    source.get("Configuration").get("OutputArtifactFormat"),
                },
                Name="Source",
            ))

    if is_s3:
        bucket_name = source.get("Configuration").get("S3Bucket")
        if not scm_skip_creation_of_repo:
            template.add_resource(
                s3.Bucket(
                    bucket_name,
                    PublicAccessBlockConfiguration=s3.
                    PublicAccessBlockConfiguration(
                        IgnorePublicAcls=True,
                        BlockPublicPolicy=True,
                        BlockPublicAcls=True,
                        RestrictPublicBuckets=True,
                    ),
                    BucketEncryption=s3.BucketEncryption(
                        ServerSideEncryptionConfiguration=[
                            s3.ServerSideEncryptionRule(
                                ServerSideEncryptionByDefault=s3.
                                ServerSideEncryptionByDefault(
                                    SSEAlgorithm="AES256"))
                        ]),
                    Tags=t.Tags.from_dict(
                        **{"ServiceCatalogPuppet:Actor": "Framework"}),
                    BucketName=bucket_name,
                    VersioningConfiguration=s3.VersioningConfiguration(
                        Status="Enabled"),
                ))

        source_stage.Actions.append(
            codepipeline.Actions(
                RunOrder=1,
                ActionTypeId=codepipeline.ActionTypeId(
                    Category="Source",
                    Owner="AWS",
                    Version="1",
                    Provider="S3",
                ),
                OutputArtifacts=[codepipeline.OutputArtifacts(Name="Source")],
                Configuration={
                    "S3Bucket":
                    bucket_name,
                    "S3ObjectKey":
                    source.get("Configuration").get("S3ObjectKey"),
                    "PollForSourceChanges":
                    source.get("Configuration").get("PollForSourceChanges"),
                },
                Name="Source",
            ))

    single_account_run_project_build_spec = dict(
        version=0.2,
        phases=dict(
            install=install_spec,
            build={
                "commands": [
                    'echo "single_account: \\"${SINGLE_ACCOUNT_ID}\\"" > parameters.yaml',
                    "cat parameters.yaml",
                    "zip parameters.zip parameters.yaml",
                    "aws s3 cp parameters.zip s3://sc-puppet-parameterised-runs-${PUPPET_ACCOUNT_ID}/parameters.zip",
                ]
            },
            post_build={
                "commands": [
                    "servicecatalog-puppet wait-for-parameterised-run-to-complete",
                ]
            },
        ),
        artifacts=dict(
            name="DeployProject",
            files=[
                "ServiceCatalogPuppet/manifest.yaml",
                "ServiceCatalogPuppet/manifest-expanded.yaml",
                "results/*/*",
                "output/*/*",
                "exploded_results/*/*",
                "tasks.log",
            ],
        ),
    )

    single_account_run_project_args = dict(
        Name="servicecatalog-puppet-single-account-run",
        Description="Runs puppet for a single account - SINGLE_ACCOUNT_ID",
        ServiceRole=t.GetAtt(deploy_role, "Arn"),
        Tags=t.Tags.from_dict(**{"ServiceCatalogPuppet:Actor": "Framework"}),
        Artifacts=codebuild.Artifacts(Type="NO_ARTIFACTS", ),
        TimeoutInMinutes=480,
        Environment=codebuild.Environment(
            ComputeType=t.Ref(deploy_environment_compute_type_parameter),
            Image="aws/codebuild/standard:4.0",
            Type="LINUX_CONTAINER",
            EnvironmentVariables=[
                {
                    "Type": "PLAINTEXT",
                    "Name": "SINGLE_ACCOUNT_ID",
                    "Value": "CHANGE_ME",
                },
            ] + deploy_env_vars,
        ),
        Source=codebuild.Source(
            Type="NO_SOURCE",
            BuildSpec=yaml.safe_dump(single_account_run_project_build_spec),
        ),
    )

    single_account_run_project = template.add_resource(
        codebuild.Project("SingleAccountRunProject",
                          **single_account_run_project_args))

    single_account_run_project_build_spec["phases"]["post_build"]["commands"] = [
        "servicecatalog-puppet wait-for-parameterised-run-to-complete --on-complete-url $CALLBACK_URL"
    ]
    single_account_run_project_args[
        "Name"] = "servicecatalog-puppet-single-account-run-with-callback"
    single_account_run_project_args[
        "Description"] = "Runs puppet for a single account - SINGLE_ACCOUNT_ID and then does a http put"
    single_account_run_project_args.get(
        "Environment").EnvironmentVariables.append({
            "Type": "PLAINTEXT",
            "Name": "CALLBACK_URL",
            "Value": "CHANGE_ME",
        })
    single_account_run_project_args["Source"] = codebuild.Source(
        Type="NO_SOURCE",
        BuildSpec=yaml.safe_dump(single_account_run_project_build_spec),
    )
    single_account_run_project_with_callback = template.add_resource(
        codebuild.Project("SingleAccountRunWithCallbackProject",
                          **single_account_run_project_args))

    stages = [source_stage]

    if should_validate:
        template.add_resource(
            codebuild.Project(
                "ValidateProject",
                Name="servicecatalog-puppet-validate",
                ServiceRole=t.GetAtt("DeployRole", "Arn"),
                Tags=t.Tags.from_dict(
                    **{"ServiceCatalogPuppet:Actor": "Framework"}),
                Artifacts=codebuild.Artifacts(Type="CODEPIPELINE"),
                TimeoutInMinutes=60,
                Environment=codebuild.Environment(
                    ComputeType="BUILD_GENERAL1_SMALL",
                    Image="aws/codebuild/standard:4.0",
                    Type="LINUX_CONTAINER",
                ),
                Source=codebuild.Source(
                    BuildSpec=yaml.safe_dump(
                        dict(
                            version="0.2",
                            phases={
                                "install": {
                                    "runtime-versions": {
                                        "python": "3.7",
                                    },
                                    "commands": [
                                        f"pip install {puppet_version}"
                                        if "http" in puppet_version else
                                        f"pip install aws-service-catalog-puppet=={puppet_version}",
                                    ],
                                },
                                "build": {
                                    "commands": [
                                        "servicecatalog-puppet validate manifest.yaml"
                                    ]
                                },
                            },
                        )),
                    Type="CODEPIPELINE",
                ),
                Description="Validate the manifest.yaml file",
            ))
        stages.append(
            codepipeline.Stages(
                Name="Validate",
                Actions=[
                    codepipeline.Actions(
                        InputArtifacts=[
                            codepipeline.InputArtifacts(Name="Source"),
                        ],
                        Name="Validate",
                        ActionTypeId=codepipeline.ActionTypeId(
                            Category="Build",
                            Owner="AWS",
                            Version="1",
                            Provider="CodeBuild",
                        ),
                        OutputArtifacts=[
                            codepipeline.OutputArtifacts(
                                Name="ValidateProject")
                        ],
                        Configuration={
                            "ProjectName": t.Ref("ValidateProject"),
                            "PrimarySource": "Source",
                        },
                        RunOrder=1,
                    ),
                ],
            ))

    if is_manual_approvals:
        deploy_stage = codepipeline.Stages(
            Name="Deploy",
            Actions=[
                codepipeline.Actions(
                    InputArtifacts=[
                        codepipeline.InputArtifacts(Name="Source"),
                        codepipeline.InputArtifacts(
                            Name="ParameterisedSource"),
                    ],
                    Name="DryRun",
                    ActionTypeId=codepipeline.ActionTypeId(
                        Category="Build",
                        Owner="AWS",
                        Version="1",
                        Provider="CodeBuild",
                    ),
                    OutputArtifacts=[
                        codepipeline.OutputArtifacts(Name="DryRunProject")
                    ],
                    Configuration={
                        "ProjectName": t.Ref("DryRunProject"),
                        "PrimarySource": "Source",
                    },
                    RunOrder=1,
                ),
                codepipeline.Actions(
                    ActionTypeId=codepipeline.ActionTypeId(
                        Category="Approval",
                        Owner="AWS",
                        Version="1",
                        Provider="Manual",
                    ),
                    Configuration={
                        "NotificationArn":
                        t.Ref("DryRunNotificationTopic"),
                        "CustomData":
                        "Approve when you are happy with the dry run.",
                    },
                    Name="DryRunApproval",
                    RunOrder=2,
                ),
                codepipeline.Actions(
                    InputArtifacts=[
                        codepipeline.InputArtifacts(Name="Source"),
                        codepipeline.InputArtifacts(
                            Name="ParameterisedSource"),
                    ],
                    Name="Deploy",
                    ActionTypeId=codepipeline.ActionTypeId(
                        Category="Build",
                        Owner="AWS",
                        Version="1",
                        Provider="CodeBuild",
                    ),
                    OutputArtifacts=[
                        codepipeline.OutputArtifacts(Name="DeployProject")
                    ],
                    Configuration={
                        "ProjectName": t.Ref("DeployProject"),
                        "PrimarySource": "Source",
                    },
                    RunOrder=3,
                ),
            ],
        )
    else:
        deploy_stage = codepipeline.Stages(
            Name="Deploy",
            Actions=[
                codepipeline.Actions(
                    InputArtifacts=[
                        codepipeline.InputArtifacts(Name="Source"),
                        codepipeline.InputArtifacts(
                            Name="ParameterisedSource"),
                    ],
                    Name="Deploy",
                    ActionTypeId=codepipeline.ActionTypeId(
                        Category="Build",
                        Owner="AWS",
                        Version="1",
                        Provider="CodeBuild",
                    ),
                    OutputArtifacts=[
                        codepipeline.OutputArtifacts(Name="DeployProject")
                    ],
                    Configuration={
                        "ProjectName":
                        t.Ref("DeployProject"),
                        "PrimarySource":
                        "Source",
                        "EnvironmentVariables":
                        '[{"name":"EXECUTION_ID","value":"#{codepipeline.PipelineExecutionId}","type":"PLAINTEXT"}]',
                    },
                    RunOrder=1,
                ),
            ],
        )

    stages.append(deploy_stage)

    pipeline = template.add_resource(
        codepipeline.Pipeline(
            "Pipeline",
            RoleArn=t.GetAtt("PipelineRole", "Arn"),
            Stages=stages,
            Name=t.Sub("${AWS::StackName}-pipeline"),
            ArtifactStore=codepipeline.ArtifactStore(
                Type="S3",
                Location=t.Sub(
                    "sc-puppet-pipeline-artifacts-${AWS::AccountId}-${AWS::Region}"
                ),
            ),
            RestartExecutionOnUpdate=True,
        ))

    if is_github:
        template.add_resource(
            codepipeline.Webhook(
                "Webhook",
                AuthenticationConfiguration=codepipeline.
                WebhookAuthConfiguration(SecretToken=t.Join(
                    "",
                    [
                        "{{resolve:secretsmanager:",
                        source.get("Configuration").get(
                            "SecretsManagerSecret"),
                        ":SecretString:SecretToken}}",
                    ],
                )),
                Filters=[
                    codepipeline.WebhookFilterRule(
                        JsonPath="$.ref",
                        MatchEquals="refs/heads/" +
                        source.get("Configuration").get("Branch"),
                    )
                ],
                Authentication="GITHUB_HMAC",
                TargetPipeline=t.Ref(pipeline),
                TargetAction="Source",
                Name=t.Sub("${AWS::StackName}-webhook"),
                TargetPipelineVersion=t.GetAtt(pipeline, "Version"),
                RegisterWithThirdParty="true",
            ))

    deploy_project_build_spec = dict(
        version=0.2,
        phases=dict(
            install={
                "runtime-versions":
                dict(python="3.7"),
                "commands": [
                    f"pip install {puppet_version}"
                    if "http" in puppet_version else
                    f"pip install aws-service-catalog-puppet=={puppet_version}",
                ],
            },
            pre_build={
                "commands": [
                    "servicecatalog-puppet --info expand --parameter-override-file $CODEBUILD_SRC_DIR_ParameterisedSource/parameters.yaml manifest.yaml",
                ]
            },
            build={
                "commands": [
                    "servicecatalog-puppet --info deploy --num-workers ${NUM_WORKERS} manifest-expanded.yaml",
                ]
            },
        ),
        artifacts=dict(
            name="DeployProject",
            files=[
                "manifest-expanded.yaml",
                "results/*/*",
                "output/*/*",
                "exploded_results/*/*",
                "tasks.log",
            ],
        ),
    )

    deploy_project_args = dict(
        Name="servicecatalog-puppet-deploy",
        ServiceRole=t.GetAtt(deploy_role, "Arn"),
        Tags=t.Tags.from_dict(**{"ServiceCatalogPuppet:Actor": "Framework"}),
        Artifacts=codebuild.Artifacts(Type="CODEPIPELINE", ),
        TimeoutInMinutes=480,
        Environment=codebuild.Environment(
            ComputeType=t.Ref(deploy_environment_compute_type_parameter),
            Image="aws/codebuild/standard:4.0",
            Type="LINUX_CONTAINER",
            EnvironmentVariables=[
                {
                    "Type": "PARAMETER_STORE",
                    "Name": "NUM_WORKERS",
                    "Value": t.Ref(num_workers_ssm_parameter),
                },
                {
                    "Type":
                    "PARAMETER_STORE",
                    "Name":
                    "SPOKE_EXECUTION_MODE_DEPLOY_ENV",
                    "Value":
                    constants.SPOKE_EXECUTION_MODE_DEPLOY_ENV_PARAMETER_NAME,
                },
            ] + deploy_env_vars,
        ),
        Source=codebuild.Source(
            Type="CODEPIPELINE",
            BuildSpec=yaml.safe_dump(deploy_project_build_spec),
        ),
        Description="deploys out the products to be deployed",
    )

    deploy_project = template.add_resource(
        codebuild.Project("DeployProject", **deploy_project_args))

    deploy_project_build_spec["phases"]["build"]["commands"] = [
        "servicecatalog-puppet --info dry-run manifest-expanded.yaml"
    ]
    deploy_project_build_spec["artifacts"]["name"] = "DryRunProject"
    deploy_project_args["Name"] = "servicecatalog-puppet-dryrun"
    deploy_project_args[
        "Description"] = "dry run of servicecatalog-puppet-dryrun"
    deploy_project_args["Source"] = codebuild.Source(
        Type="CODEPIPELINE",
        BuildSpec=yaml.safe_dump(deploy_project_build_spec),
    )

    dry_run_project = template.add_resource(
        codebuild.Project("DryRunProject", **deploy_project_args))

    bootstrap_project = template.add_resource(
        codebuild.Project(
            "BootstrapProject",
            Name="servicecatalog-puppet-bootstrap-spokes-in-ou",
            ServiceRole=t.GetAtt("DeployRole", "Arn"),
            Tags=t.Tags.from_dict(
                **{"ServiceCatalogPuppet:Actor": "Framework"}),
            Artifacts=codebuild.Artifacts(Type="NO_ARTIFACTS"),
            TimeoutInMinutes=60,
            Environment=codebuild.Environment(
                ComputeType="BUILD_GENERAL1_SMALL",
                Image="aws/codebuild/standard:4.0",
                Type="LINUX_CONTAINER",
                EnvironmentVariables=[
                    {
                        "Type": "PLAINTEXT",
                        "Name": "OU_OR_PATH",
                        "Value": "CHANGE_ME"
                    },
                    {
                        "Type": "PLAINTEXT",
                        "Name": "IAM_ROLE_NAME",
                        "Value": "OrganizationAccountAccessRole",
                    },
                    {
                        "Type": "PLAINTEXT",
                        "Name": "IAM_ROLE_ARNS",
                        "Value": ""
                    },
                ],
            ),
            Source=codebuild.Source(
                BuildSpec=
                "version: 0.2\nphases:\n  install:\n    runtime-versions:\n      python: 3.7\n    commands:\n      - pip install aws-service-catalog-puppet\n  build:\n    commands:\n      - servicecatalog-puppet bootstrap-spokes-in-ou $OU_OR_PATH $IAM_ROLE_NAME $IAM_ROLE_ARNS\nartifacts:\n  files:\n    - results/*/*\n    - output/*/*\n  name: BootstrapProject\n",
                Type="NO_SOURCE",
            ),
            Description="Bootstrap all the accounts in an OU",
        ))

    template.add_resource(
        codebuild.Project(
            "BootstrapASpokeProject",
            Name="servicecatalog-puppet-bootstrap-spoke",
            ServiceRole=t.GetAtt("DeployRole", "Arn"),
            Tags=t.Tags.from_dict(
                **{"ServiceCatalogPuppet:Actor": "Framework"}),
            Artifacts=codebuild.Artifacts(Type="NO_ARTIFACTS"),
            TimeoutInMinutes=60,
            Environment=codebuild.Environment(
                ComputeType="BUILD_GENERAL1_SMALL",
                Image="aws/codebuild/standard:4.0",
                Type="LINUX_CONTAINER",
                EnvironmentVariables=[
                    {
                        "Type": "PLAINTEXT",
                        "Name": "PUPPET_ACCOUNT_ID",
                        "Value": t.Sub("${AWS::AccountId}"),
                    },
                    {
                        "Type": "PLAINTEXT",
                        "Name": "ORGANIZATION_ACCOUNT_ACCESS_ROLE_ARN",
                        "Value": "CHANGE_ME",
                    },
                    {
                        "Type": "PLAINTEXT",
                        "Name": "ASSUMABLE_ROLE_IN_ROOT_ACCOUNT",
                        "Value": "CHANGE_ME",
                    },
                ],
            ),
            Source=codebuild.Source(
                BuildSpec=yaml.safe_dump(
                    dict(
                        version=0.2,
                        phases=dict(
                            install=install_spec,
                            build={
                                "commands": [
                                    "servicecatalog-puppet bootstrap-spoke-as ${PUPPET_ACCOUNT_ID} ${ASSUMABLE_ROLE_IN_ROOT_ACCOUNT} ${ORGANIZATION_ACCOUNT_ACCESS_ROLE_ARN}"
                                ]
                            },
                        ),
                    )),
                Type="NO_SOURCE",
            ),
            Description="Bootstrap given account as a spoke",
        ))

    cloud_formation_events_queue = template.add_resource(
        sqs.Queue(
            "CloudFormationEventsQueue",
            QueueName="servicecatalog-puppet-cloudformation-events",
            Tags=t.Tags.from_dict(
                **{"ServiceCatalogPuppet:Actor": "Framework"}),
        ))

    cloud_formation_events_queue_policy = template.add_resource(
        sqs.QueuePolicy(
            "CloudFormationEventsQueuePolicy",
            Queues=[t.Ref(cloud_formation_events_queue)],
            PolicyDocument={
                "Id":
                "AllowSNS",
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Sid": "allow-send-message",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "*"
                    },
                    "Action": ["sqs:SendMessage"],
                    "Resource": "*",
                    "Condition": {
                        "ArnEquals": {
                            "aws:SourceArn":
                            t.Sub(
                                "arn:${AWS::Partition}:sns:*:${AWS::AccountId}:servicecatalog-puppet-cloudformation-regional-events"
                            )
                        }
                    },
                }],
            },
        ))

    spoke_deploy_bucket = template.add_resource(
        s3.Bucket(
            "SpokeDeployBucket",
            PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration(
                IgnorePublicAcls=True,
                BlockPublicPolicy=True,
                BlockPublicAcls=True,
                RestrictPublicBuckets=True,
            ),
            BucketEncryption=s3.BucketEncryption(
                ServerSideEncryptionConfiguration=[
                    s3.ServerSideEncryptionRule(
                        ServerSideEncryptionByDefault=s3.
                        ServerSideEncryptionByDefault(SSEAlgorithm="AES256"))
                ]),
            Tags=t.Tags.from_dict(
                **{"ServiceCatalogPuppet:Actor": "Framework"}),
            BucketName=t.Sub("sc-puppet-spoke-deploy-${AWS::AccountId}"),
            VersioningConfiguration=s3.VersioningConfiguration(
                Status="Enabled"),
        ))

    caching_bucket = template.add_resource(
        s3.Bucket(
            "CachingBucket",
            PublicAccessBlockConfiguration=s3.PublicAccessBlockConfiguration(
                BlockPublicAcls=True,
                BlockPublicPolicy=True,
                IgnorePublicAcls=True,
                RestrictPublicBuckets=True,
            ),
            BucketEncryption=s3.BucketEncryption(
                ServerSideEncryptionConfiguration=[
                    s3.ServerSideEncryptionRule(
                        ServerSideEncryptionByDefault=s3.
                        ServerSideEncryptionByDefault(SSEAlgorithm="AES256"))
                ]),
            Tags=t.Tags.from_dict(
                **{"ServiceCatalogPuppet:Actor": "Framework"}),
            BucketName=t.Sub(
                "sc-puppet-caching-bucket-${AWS::AccountId}-${AWS::Region}"),
            VersioningConfiguration=s3.VersioningConfiguration(
                Status="Enabled"),
        ))

    template.add_output(
        t.Output(
            "CloudFormationEventsQueueArn",
            Value=t.GetAtt(cloud_formation_events_queue, "Arn"),
        ))
    template.add_output(t.Output("Version", Value=t.GetAtt(param, "Value")))
    template.add_output(
        t.Output("ManualApprovalsParam",
                 Value=t.GetAtt(manual_approvals_param, "Value")))

    template.add_resource(
        ssm.Parameter(
            "DefaultTerraformVersion",
            Type="String",
            Name=constants.DEFAULT_TERRAFORM_VERSION_PARAMETER_NAME,
            Value=constants.DEFAULT_TERRAFORM_VERSION_VALUE,
        ))

    return template
示例#18
0
    def handle(self, chain_context):
        """
        This step adds in the shell of a pipeline.
         * s3 bucket
         * policies for the bucket and pipeline
         * your next step in the chain MUST be a source stage
        :param chain_context:
        :return:
        """

        if self.create_bucket:
            pipeline_bucket = Bucket(
                "PipelineBucket%s" % chain_context.instance_name,
                BucketName=self.bucket_name,
                VersioningConfiguration=VersioningConfiguration(
                    Status="Enabled"))
            chain_context.template.add_resource(pipeline_bucket)

        default_bucket_policies = self.get_default_bucket_policy_statements(
            self.bucket_name)

        if self.bucket_policy_statements:
            bucket_access_policy = self.get_bucket_policy(
                pipeline_bucket=self.bucket_name,
                bucket_policy_statements=self.bucket_policy_statements,
            )
            chain_context.template.add_resource(bucket_access_policy)

        pipeline_bucket_access_policy = iam.ManagedPolicy(
            "PipelineBucketAccessPolicy",
            Path='/managed/',
            PolicyDocument=awacs.aws.PolicyDocument(
                Version="2012-10-17",
                Id="bucket-access-policy%s" % chain_context.instance_name,
                Statement=default_bucket_policies))

        chain_context.metadata[cumulus.steps.dev_tools.
                               META_PIPELINE_BUCKET_NAME] = self.bucket_name
        chain_context.metadata[
            cumulus.steps.dev_tools.META_PIPELINE_BUCKET_POLICY_REF] = Ref(
                pipeline_bucket_access_policy)

        # TODO: this can be cleaned up by using a policytype and passing in the pipeline role it should add itself to.
        pipeline_policy = iam.Policy(
            PolicyName="%sPolicy" % self.name,
            PolicyDocument=awacs.aws.PolicyDocument(
                Version="2012-10-17",
                Id="PipelinePolicy",
                Statement=[
                    awacs.aws.Statement(
                        Effect=awacs.aws.Allow,
                        # TODO: actions here could be limited more
                        Action=[awacs.aws.Action("s3", "*")],
                        Resource=[
                            troposphere.Join(
                                '', [awacs.s3.ARN(), self.bucket_name, "/*"]),
                            troposphere.Join('', [
                                awacs.s3.ARN(),
                                self.bucket_name,
                            ]),
                        ],
                    ),
                    awacs.aws.Statement(
                        Effect=awacs.aws.Allow,
                        Action=[awacs.aws.Action("kms", "*")],
                        Resource=['*'],
                    ),
                    awacs.aws.Statement(
                        Effect=awacs.aws.Allow,
                        Action=[
                            awacs.aws.Action("cloudformation", "*"),
                            awacs.aws.Action("codebuild", "*"),
                        ],
                        # TODO: restrict more accurately
                        Resource=["*"]),
                    awacs.aws.Statement(
                        Effect=awacs.aws.Allow,
                        Action=[
                            awacs.codecommit.GetBranch,
                            awacs.codecommit.GetCommit,
                            awacs.codecommit.UploadArchive,
                            awacs.codecommit.GetUploadArchiveStatus,
                            awacs.codecommit.CancelUploadArchive
                        ],
                        Resource=["*"]),
                    awacs.aws.Statement(Effect=awacs.aws.Allow,
                                        Action=[awacs.iam.PassRole],
                                        Resource=["*"]),
                    awacs.aws.Statement(
                        Effect=awacs.aws.Allow,
                        Action=[awacs.aws.Action("lambda", "*")],
                        Resource=["*"])
                ],
            ))

        pipeline_service_role = iam.Role(
            "PipelineServiceRole",
            Path="/",
            AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[
                awacs.aws.Statement(Effect=awacs.aws.Allow,
                                    Action=[awacs.sts.AssumeRole],
                                    Principal=awacs.aws.Principal(
                                        'Service',
                                        "codepipeline.amazonaws.com"))
            ]),
            Policies=[pipeline_policy] + self.pipeline_policies)

        generic_pipeline = codepipeline.Pipeline(
            "Pipeline",
            RoleArn=troposphere.GetAtt(pipeline_service_role, "Arn"),
            Stages=[],
            ArtifactStore=codepipeline.ArtifactStore(
                Type="S3",
                Location=self.bucket_name,
            )
            # TODO: optionally add kms key here
        )

        if self.bucket_kms_key_arn:
            encryption_config = codepipeline.EncryptionKey(
                "ArtifactBucketKmsKey",
                Id=self.bucket_kms_key_arn,
                Type='KMS',
            )
            generic_pipeline.ArtifactStore.EncryptionKey = encryption_config

        pipeline_output = troposphere.Output(
            "PipelineName",
            Description="Code Pipeline",
            Value=Ref(generic_pipeline),
        )
        pipeline_bucket_output = troposphere.Output(
            "PipelineBucket",
            Description="Name of the input artifact bucket for the pipeline",
            Value=self.bucket_name,
        )

        chain_context.template.add_resource(pipeline_bucket_access_policy)
        chain_context.template.add_resource(pipeline_service_role)
        chain_context.template.add_resource(generic_pipeline)
        chain_context.template.add_output(pipeline_output)
        chain_context.template.add_output(pipeline_bucket_output)
         "Allow",
         "Principal": {
             "Service": "s3.amazonaws.com"
         },
         "Action":
         "SQS:SendMessage",
         "Resource":
         troposphere.GetAtt(ManifestQueue, "Arn"),
         "Condition":
         troposphere.If(
             "NeedsArchiveBucket", {
                 "ForAllValues:ArnLike": {
                     "aws:SourceArn":
                     troposphere.Join("", [
                         "arn:aws:s3:::",
                         troposphere.Ref(troposphere.AWS_STACK_NAME),
                         "-archivebucket-*"
                     ])
                 }
             }, {
                 "ForAllValues:ArnEquals": {
                     "aws:SourceArn":
                     troposphere.Join("", [
                         "arn:aws:s3:::",
                         troposphere.Ref(S3ArchiveBucket)
                     ])
                 }
             })
     }]
 },
 DeletionPolicy=troposphere.Retain,
示例#20
0
    def __init__(self, stack, paco_ctx, role):
        super().__init__(stack, paco_ctx)
        self.set_aws_name('IoTAnalyticsPipeline', self.resource_group_name,
                          self.resource_name)
        iotap = self.resource

        # Init Troposphere template
        self.init_template('IoT Analytics pipeline')
        if not iotap.is_enabled():
            return

        # Role ARN for IoT
        role_arn_param = self.create_cfn_parameter(
            param_type='String',
            name='IoTRoleArn',
            description='IoT Topic Rule Service Role ARN',
            value=role.get_arn(),
        )

        # Channel Resource
        iotchannel_logical_id = 'IoTAnalyticsChannel'
        cfn_export_dict = {}
        if iotap.channel_storage.bucket == None:
            channel_storage_dict = {'ServiceManagedS3': {}}
            cfn_export_dict['RetentionPeriod'] = convert_expire_to_cfn_dict(
                iotap.channel_storage.expire_events_after_days)
        else:
            channel_bucket_param = self.create_cfn_parameter(
                param_type='String',
                name='IoTAnalyticsChannelBucketName',
                description='IoT Analytics Channel storage bucket name',
                value=iotap.channel_storage.bucket + '.name',
            )
            channel_storage_dict = {
                'CustomerManagedS3': {
                    'Bucket': troposphere.Ref(channel_bucket_param),
                    'KeyPrefix': iotap.channel_storage.key_prefix,
                    'RoleArn': troposphere.Ref(role_arn_param),
                }
            }
        cfn_export_dict['ChannelStorage'] = channel_storage_dict
        iot_channel_resource = troposphere.iotanalytics.Channel.from_dict(
            iotchannel_logical_id, cfn_export_dict)
        self.template.add_resource(iot_channel_resource)

        self.create_output(
            title='ChannelName',
            description='IoT Analytics Channel name',
            value=troposphere.Ref(iot_channel_resource),
            ref=self.resource.paco_ref_parts + '.channel.name',
        )

        # Datastore Resource
        iotchannel_logical_id = 'IoTAnalyticsDatastore'
        cfn_export_dict = {}
        if iotap.datastore_storage.bucket == None:
            datastore_storage_dict = {'ServiceManagedS3': {}}
            cfn_export_dict['RetentionPeriod'] = convert_expire_to_cfn_dict(
                iotap.datastore_storage.expire_events_after_days)
        else:
            datastore_bucket_param = self.create_cfn_parameter(
                param_type='String',
                name='IoTAnalyticsDatastoreBucketName',
                description='IoT Analytics Datastore storage bucket name',
                value=iotap.datastore_storage.bucket + '.name',
            )
            datastore_storage_dict = {
                'CustomerManagedS3': {
                    'Bucket': troposphere.Ref(datastore_bucket_param),
                    'KeyPrefix': iotap.datastore_storage.key_prefix,
                    'RoleArn': troposphere.Ref(role_arn_param),
                }
            }

        cfn_export_dict['DatastoreStorage'] = datastore_storage_dict
        if iotap.datastore_name != None:
            cfn_export_dict['DatastoreName'] = iotap.datastore_name
        iotap_datastore_resource = troposphere.iotanalytics.Datastore.from_dict(
            iotchannel_logical_id, cfn_export_dict)
        iotap_datastore_resource.DependsOn = iot_channel_resource
        self.template.add_resource(iotap_datastore_resource)

        self.create_output(
            title='DatastoreName',
            description='IoT Analytics Datastore name',
            value=troposphere.Ref(iotap_datastore_resource),
            ref=self.resource.paco_ref_parts + '.datastore.name',
        )

        # Pipeline Resource
        iotpipeline_logical_id = 'IoTAnalyticsPipeline'
        cfn_export_dict = {}
        cfn_export_dict['PipelineActivities'] = []
        idx = 0
        activity_list = list(iotap.pipeline_activities.values())

        # start with a Channel activity
        if len(activity_list) == 0:
            next_name = "DatastoreActivity"
        else:
            next_name = activity_list[idx].name + "Activity"
        cfn_export_dict['PipelineActivities'].append({
            'Channel': {
                'Name': "ChannelActivity",
                'ChannelName': troposphere.Ref(iot_channel_resource),
                'Next': next_name,
            }
        })

        for activity in iotap.pipeline_activities.values():
            if len(activity_list) == idx + 1:
                next_name = 'DatastoreActivity'
            else:
                next_name = activity_list[idx + 1].name + "Activity"
            if activity.activity_type == 'lambda':
                lambda_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'LambdaFunction{idx}',
                    description=f'IoT Analytics Lambda for Activity {idx}',
                    value=activity.function + '.arn',
                )
                if not activity.batch_size:
                    activity.batch_size = 1
                activity_dict = {
                    'Lambda': {
                        'LambdaName':
                        troposphere.Join('', [
                            '',
                            troposphere.Select(
                                6,
                                troposphere.Split(
                                    ':', troposphere.Ref(lambda_param)))
                        ]),
                        'BatchSize':
                        activity.batch_size,
                        'Name':
                        activity.name + "Activity",
                        'Next':
                        next_name,
                    }
                }
            elif activity.activity_type == 'add_attributes':
                activity_dict = {
                    'AddAttributes': {
                        'Name': activity.name + "Activity",
                        'Attributes': activity.attributes,
                        'Next': next_name,
                    }
                }
            elif activity.activity_type == 'remove_attributes':
                activity_dict = {
                    'RemoveAttributes': {
                        'Name': activity.name + "Activity",
                        'Attributes': activity.attribute_list,
                        'Next': next_name,
                    }
                }
            elif activity.activity_type == 'select_attributes':
                activity_dict = {
                    'SelectAttributes': {
                        'Name': activity.name + "Activity",
                        'Attributes': activity.attribute_list,
                        'Next': next_name,
                    }
                }
            elif activity.activity_type == 'filter':
                activity_dict = {
                    'Filter': {
                        'Name': activity.name + "Activity",
                        'Filter': activity.filter,
                        'Next': next_name,
                    }
                }
            elif activity.activity_type == 'math':
                activity_dict = {
                    'Math': {
                        'Name': activity.name + "Activity",
                        'Attribute': activity.attribute,
                        'Math': activity.math,
                        'Next': next_name,
                    }
                }
            elif activity.activity_type == 'device_registry_enrich':
                activity_dict = {
                    'DeviceRegistryEnrich': {
                        'Name': activity.name + "Activity",
                        'Attribute': activity.attribute,
                        'ThingName': activity.thing_name,
                        'Next': next_name,
                    }
                }
            elif activity.activity_type == 'device_shadow_enrich':
                activity_dict = {
                    'DeviceShadowEnrich': {
                        'Name': activity.name + "Activity",
                        'Attribute': activity.attribute,
                        'ThingName': activity.thing_name,
                        'Next': next_name,
                    }
                }

            cfn_export_dict['PipelineActivities'].append(activity_dict)
            idx += 1

        # finish with a Datastore activity
        cfn_export_dict['PipelineActivities'].append({
            'Datastore': {
                'Name': "DatastoreActivity",
                'DatastoreName': troposphere.Ref(iotap_datastore_resource),
            }
        })

        iotpipeline_resource = troposphere.iotanalytics.Pipeline.from_dict(
            iotpipeline_logical_id,
            cfn_export_dict,
        )
        iotpipeline_resource.DependsOn = [
            iot_channel_resource, iotap_datastore_resource
        ]
        self.template.add_resource(iotpipeline_resource)

        self.create_output(
            title='PipelineName',
            description='IoT Analytics Pipeline name',
            value=troposphere.Ref(iotpipeline_resource),
            ref=self.resource.paco_ref_parts + '.pipeline.name',
        )

        # Datasets
        for dataset in iotap.datasets.values():
            iotdataset_logical_id = self.create_cfn_logical_id(
                f'IoTDataset{dataset.name}')
            cfn_export_dict = {}
            cfn_export_dict['Actions'] = []
            if dataset.query_action != None:
                cfn_export_dict['Actions'].append({
                    'ActionName': dataset.name,
                    'QueryAction': {
                        'Filters': dataset.query_action.filters,
                        'SqlQuery': dataset.query_action.sql_query,
                    }
                })
            else:
                # ToDo: container_action
                pass
            cfn_export_dict['ContentDeliveryRules'] = []
            for delivery_rule in dataset.content_delivery_rules.values():
                delivery_dict = {
                    'Destination': {},
                    #    'EntryName': delivery_rule.name,
                }
                if delivery_rule.s3_destination != None:
                    bucket = get_model_obj_from_ref(
                        delivery_rule.s3_destination.bucket,
                        self.paco_ctx.project)
                    delivery_dict['Destination'][
                        'S3DestinationConfiguration'] = {
                            'Bucket': bucket.get_aws_name(),
                            'Key': delivery_rule.s3_destination.key,
                            'RoleArn': troposphere.Ref(role_arn_param),
                        }
                cfn_export_dict['ContentDeliveryRules'].append(delivery_dict)

            cfn_export_dict['RetentionPeriod'] = convert_expire_to_cfn_dict(
                dataset.expire_events_after_days)
            if dataset.version_history != None:
                if dataset.version_history == 0:
                    cfn_export_dict['VersioningConfiguration'] = {
                        'Unlimited': True
                    }
                else:
                    cfn_export_dict['VersioningConfiguration'] = {
                        'MaxVersions': dataset.version_history,
                        'Unlimited': False
                    }

            iot_dataset_resource = troposphere.iotanalytics.Dataset.from_dict(
                iotdataset_logical_id, cfn_export_dict)
            iot_dataset_resource.DependsOn = iotap_datastore_resource
            self.template.add_resource(iot_dataset_resource)

            self.create_output(
                title=f'{dataset.name}DatasetName',
                description=f'IoT Analytics Dataset {dataset.name}',
                value=troposphere.Ref(iot_dataset_resource),
                ref=self.resource.paco_ref_parts + '.dataset.' + dataset.name +
                '.name',
            )
示例#21
0
    def __init__(self, stack, paco_ctx):
        super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_IAM"])
        self.apigatewayrestapi = apigatewayrestapi = stack.resource
        self.set_aws_name('ApiGatewayRestApi', self.resource_group_name, self.resource.name)

        self.init_template('ApiGateway: {}'.format(apigatewayrestapi.title))
        template = self.template
        if not self.apigatewayrestapi.is_enabled():
            return

        # Parameters
        method_params = []
        for method in self.apigatewayrestapi.methods.values():
            param_name = 'MethodArn' + self.create_cfn_logical_id(method.name)
            lambda_arn_param = self.create_cfn_parameter(
                name=param_name,
                param_type='String',
                description='Lambda ARN parameter.',
                value=method.integration.integration_lambda + '.arn',
            )
            method.parameter_arn_ref = troposphere.Ref(param_name)

        # Resources
        restapi_logical_id = 'ApiGatewayRestApi'
        restapi_resource = troposphere.apigateway.RestApi.from_dict(
            restapi_logical_id,
            self.apigatewayrestapi.cfn_export_dict
        )
        template.add_resource(restapi_resource)

        # Model
        for model in self.apigatewayrestapi.models.values():
            model.logical_id = self.create_cfn_logical_id('ApiGatewayModel' + model.name)
            cfn_export_dict = model.cfn_export_dict
            cfn_export_dict['RestApiId'] = troposphere.Ref(restapi_resource)
            if 'Schema' not in cfn_export_dict:
                cfn_export_dict['Schema'] = {}
            model_resource = troposphere.apigateway.Model.from_dict(model.logical_id, cfn_export_dict)
            model.resource = model_resource
            template.add_resource(model_resource)

        # Resource
        for resource in self.apigatewayrestapi.resources.values():
            resource_id = 'ApiGatewayResource' + self.create_cfn_logical_id(resource.name)
            cfn_export_dict = resource.cfn_export_dict
            if resource.parent_id == "RootResourceId":
                cfn_export_dict["ParentId"] = troposphere.GetAtt(restapi_resource, "RootResourceId")
                cfn_export_dict["RestApiId"] = troposphere.Ref(restapi_resource)
            else:
                raise NotImplemented("ToDo: handle nested resources")
            resource_resource = troposphere.apigateway.Resource.from_dict(resource_id, cfn_export_dict)
            resource.resource = resource_resource
            resource_resource.DependsOn = restapi_logical_id
            template.add_resource(resource_resource)

        # Method
        for method in self.apigatewayrestapi.methods.values():
            method_id = 'ApiGatewayMethod' + self.create_cfn_logical_id(method.name)
            method.logical_id = method_id
            cfn_export_dict = method.cfn_export_dict
            for resource in self.apigatewayrestapi.resources.values():
                if resource.name == method.resource_id:
                    cfn_export_dict["ResourceId"] = troposphere.Ref(resource.resource)
            if 'ResourceId' not in cfn_export_dict:
                cfn_export_dict["ResourceId"] = troposphere.GetAtt(restapi_resource, 'RootResourceId')
            cfn_export_dict["RestApiId"] = troposphere.Ref(restapi_resource)
            uri = troposphere.Join('', ["arn:aws:apigateway:", method.region_name, ":lambda:path/2015-03-31/functions/", method.parameter_arn_ref, "/invocations"])
            cfn_export_dict["Integration"]["Uri"] = uri

            if method.integration.integration_type == 'AWS_PROXY':
                # IAM Role - allows API Gateway to invoke Lambda
                # ToDo: enable Api Gateway to invoke things other than Lambda ...
                iam_role_resource = troposphere.iam.Role(
                    self.create_cfn_logical_id('ApiGatewayIamRole' + self.apigatewayrestapi.name + method.name),
                    Path='/',
                    AssumeRolePolicyDocument=Policy(
                        Version='2012-10-17',
                        Statement=[
                            Statement(
                                Effect=Allow,
                                Action=[awacs.sts.AssumeRole],
                                Principal=Principal('Service',['apigateway.amazonaws.com'])
                            )
                        ],
                    ),
                    Policies=[
                        troposphere.iam.Policy(
                            PolicyName=self.create_cfn_logical_id('LambdaAccessApiGateway' + self.apigatewayrestapi.name + method.name),
                            PolicyDocument=Policy(
                                Version='2012-10-17',
                                Statement=[
                                    Statement(
                                        Effect=Allow,
                                        Action=[awacs.awslambda.InvokeFunction],
                                        Resource=[method.parameter_arn_ref],
                                    )
                                ]
                            )
                        )
                    ]
                )
                template.add_resource(iam_role_resource)
                cfn_export_dict["Integration"]["Credentials"] = troposphere.GetAtt(iam_role_resource, "Arn")

            elif method.integration.integration_type == 'AWS':
                # Enable Lambda (custom) integration
                # When send to a Lambda (Custom) the HTTP Method must always be POST regardless of
                # the HttpMethod
                cfn_export_dict["Integration"]["IntegrationHttpMethod"] = "POST"
                lambda_permission_resource = troposphere.awslambda.Permission(
                    self.create_cfn_logical_id('LambdaPermissionApiGateway' + method.name),
                    Action = 'lambda:InvokeFunction',
                    FunctionName = method.parameter_arn_ref,
                    Principal = 'apigateway.amazonaws.com',
                    SourceArn = troposphere.Sub(
                        "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${%s}/*/%s/" % (
                            restapi_logical_id, method.http_method
                        )
                    )
                )
                template.add_resource(lambda_permission_resource)

            # look-up the method_names and assign a Ref to the model resource
            # ToDo: validate model_names in the model
            responses = []
            for method_response in method.method_responses:
                response_dict = {"StatusCode": method_response.status_code}
                if method_response.response_models:
                    response_dict["ResponseModels"] = {}
                    for response_model in method_response.response_models:
                        for model in self.apigatewayrestapi.models.values():
                            if model.name == response_model.model_name:
                                response_dict["ResponseModels"][response_model.content_type] = troposphere.Ref(model.resource)
                responses.append(response_dict)
            cfn_export_dict["MethodResponses"] = responses

            method_resource = troposphere.apigateway.Method.from_dict(method_id, cfn_export_dict)
            method_resource.DependsOn = restapi_logical_id
            template.add_resource(method_resource)

        # Deployment
        deployment_resource = troposphere.apigateway.Deployment.from_dict(
            'ApiGatewayDeployment',
            {'Description': 'Deployment',
             'RestApiId': troposphere.Ref(restapi_resource) }
        )
        # ToDo: Deployment depends upon all Methods
        for method in self.apigatewayrestapi.methods.values():
            deployment_resource.DependsOn = method.logical_id
        template.add_resource(deployment_resource)

        # Stage
        for stage in self.apigatewayrestapi.stages.values():
            stage_id = self.create_cfn_logical_id('ApiGatewayStage' + stage.name)
            cfn_export_dict = stage.cfn_export_dict
            cfn_export_dict["RestApiId"] = troposphere.Ref(restapi_resource)
            cfn_export_dict["DeploymentId"] = troposphere.Ref(deployment_resource)
            stage_resource = troposphere.apigateway.Stage.from_dict(stage_id, cfn_export_dict)
            template.add_resource(stage_resource)
示例#22
0
文件: rds.py 项目: dwtcourses/paco
    def __init__(self, stack, paco_ctx,):
        rds_aurora = stack.resource
        super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_IAM"])
        self.set_aws_name('RDSAurora', self.resource_group_name, self.resource.name)
        self.init_template('RDSAurora')
        if not rds_aurora.is_enabled(): return

        rds_cluster_logical_id = 'DBCluster'
        db_cluster_dict = rds_aurora.cfn_export_dict
        self.notification_groups = {}

        # DB Subnet Group
        db_subnet_id_list_param = self.create_cfn_parameter(
            param_type='List<AWS::EC2::Subnet::Id>',
            name='DBSubnetIdList',
            description='The list of subnet IDs where this database will be provisioned.',
            value=rds_aurora.segment + '.subnet_id_list',
        )
        db_subnet_group_resource = troposphere.rds.DBSubnetGroup(
            title='DBSubnetGroup',
            template=self.template,
            DBSubnetGroupDescription=troposphere.Ref('AWS::StackName'),
            SubnetIds=troposphere.Ref(db_subnet_id_list_param),
        )
        db_cluster_dict['DBSubnetGroupName'] = troposphere.Ref(db_subnet_group_resource)

        # DB Cluster Parameter Group
        if rds_aurora.cluster_parameter_group == None:
            # If no Cluster Parameter Group supplied then create one
            param_group_family = gen_vocabulary.rds_engine_versions[rds_aurora.engine][rds_aurora.engine_version]['param_group_family']
            cluster_parameter_group_ref = troposphere.rds.DBClusterParameterGroup(
                "DBClusterParameterGroup",
                template=self.template,
                Family=param_group_family,
                Description=troposphere.Ref('AWS::StackName')
            )
        else:
            # Use existing Parameter Group
            cluster_parameter_group_ref = self.create_cfn_parameter(
                name='DBClusterParameterGroupName',
                param_type='String',
                description='DB Cluster Parameter Group Name',
                value=rds_aurora.cluster_parameter_group + '.name',
            )
        db_cluster_dict['DBClusterParameterGroupName'] = troposphere.Ref(cluster_parameter_group_ref)

        # Default DB Parameter Group
        need_db_pg = False
        default_instance = rds_aurora.default_instance
        for db_instance in rds_aurora.db_instances.values():
            if default_instance.parameter_group == None and db_instance.parameter_group == None:
                need_db_pg = True
        if need_db_pg:
            # create default DB Parameter Group
            param_group_family = gen_vocabulary.rds_engine_versions[rds_config.engine][rds_config.engine_version]['param_group_family']
            default_dbparametergroup_resource = troposphere.rds.DBParameterGroup(
                "DBParameterGroup",
                template=self.template,
                Family=param_group_family,
                Description=troposphere.Ref('AWS::StackName')
            )

        # Enhanced Monitoring Role
        need_monitoring_role = False
        for db_instance in rds_aurora.db_instances.values():
            enhanced_monitoring_interval = db_instance.get_value_or_default('enhanced_monitoring_interval_in_seconds')
            if enhanced_monitoring_interval != 0:
                need_monitoring_role = True
        if need_monitoring_role:
            enhanced_monitoring_role_resource = troposphere.iam.Role(
                title='MonitoringIAMRole',
                template=self.template,
                AssumeRolePolicyDocument=PolicyDocument(
                    Statement=[
                        Statement(
                            Effect=Allow,
                            Action=[Action("sts", "AssumeRole")],
                            Principal=Principal("Service", "monitoring.rds.amazonaws.com")
                        )
                    ]
                ),
                ManagedPolicyArns=["arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"],
                Path="/",
            )

        # DB Snapshot Identifier
        if rds_aurora.db_snapshot_identifier == '' or rds_aurora.db_snapshot_identifier == None:
            db_snapshot_id_enabled = False
        else:
            db_snapshot_id_enabled = True
        if db_snapshot_id_enabled == True:
            db_cluster_dict['SnapshotIdentifier'] = rds_aurora.db_snapshot_identifier

        # KMS-CMK key encryption
        if rds_aurora.enable_kms_encryption == True and db_snapshot_id_enabled == False:
            key_policy = Policy(
                Version='2012-10-17',
                Statement=[
                    Statement(
                        Effect=Allow,
                        Action=[Action('kms', '*'),],
                        Principal=Principal("AWS", [f'arn:aws:iam::{self.stack.account_ctx.id}:root']),
                        Resource=['*'],
                    ),
                    Statement(
                        Effect=Allow,
                        Action=[
                            awacs.kms.Encrypt,
                            awacs.kms.Decrypt,
                            Action('kms', 'ReEncrypt*'),
                            Action('kms', 'GenerateDataKey*'),
                            awacs.kms.CreateGrant,
                            awacs.kms.ListGrants,
                            awacs.kms.DescribeKey,
                        ],
                        Principal=Principal('AWS',['*']),
                        Resource=['*'],
                        Condition=Condition([
                            StringEquals({
                                'kms:CallerAccount': f'{self.stack.account_ctx.id}',
                                'kms:ViaService': f'rds.{self.stack.aws_region}.amazonaws.com'
                            })
                        ]),
                    ),
                ],
            )
            kms_key_resource = troposphere.kms.Key(
                title='AuroraKMSCMK',
                template=self.template,
                KeyPolicy=key_policy,
            )
            db_cluster_dict['StorageEncrypted'] = True
            db_cluster_dict['KmsKeyId'] = troposphere.Ref(kms_key_resource)

            kms_key_alias_resource = troposphere.kms.Alias(
                title="AuroraKMSCMKAlias",
                template=self.template,
                AliasName=troposphere.Sub('alias/${' + rds_cluster_logical_id + '}'),
                TargetKeyId=troposphere.Ref(kms_key_resource),
            )
            kms_key_alias_resource.DependsOn = rds_cluster_logical_id

        # Username and Passsword - only if there is no DB Snapshot Identifier
        if db_snapshot_id_enabled == False:
            db_cluster_dict['MasterUsername'] = rds_aurora.master_username
            if rds_aurora.secrets_password:
                # Password from Secrets Manager
                sta_logical_id = 'SecretTargetAttachmentRDS'
                secret_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name='RDSSecretARN',
                    description='The ARN for the secret for the RDS master password.',
                    value=rds_aurora.secrets_password + '.arn',
                )
                secret_target_attachment_resource = troposphere.secretsmanager.SecretTargetAttachment(
                    title=sta_logical_id,
                    template=self.template,
                    SecretId=troposphere.Ref(secret_arn_param),
                    TargetId=troposphere.Ref(rds_cluster_logical_id),
                    TargetType='AWS::RDS::DBCluster'
                )
                secret_target_attachment_resource.DependsOn = rds_cluster_logical_id
                db_cluster_dict['MasterUserPassword'] = troposphere.Join(
                    '',
                    ['{{resolve:secretsmanager:', troposphere.Ref(secret_arn_param), ':SecretString:password}}' ]
                )
            else:
                master_password_param = self.create_cfn_parameter(
                    param_type='String',
                    name='MasterUserPassword',
                    description='The master user password.',
                    value=rds_aurora.master_user_password,
                    noecho=True,
                )
                db_cluster_dict['MasterUserPassword'] = troposphere.Ref(master_password_param)

        db_cluster_res = troposphere.rds.DBCluster.from_dict(
            rds_cluster_logical_id,
            db_cluster_dict
        )
        self.template.add_resource(db_cluster_res)

        # Cluster Event Notifications
        if hasattr(rds_aurora, 'cluster_event_notifications'):
            for group in rds_aurora.cluster_event_notifications.groups:
                notif_param = self.create_notification_param(group)
                event_subscription_resource = troposphere.rds.EventSubscription(
                    title=self.create_cfn_logical_id(f"ClusterEventSubscription{group}"),
                    template=self.template,
                    EventCategories=rds_aurora.cluster_event_notifications.event_categories,
                    SourceIds=[troposphere.Ref(db_cluster_res)],
                    SnsTopicArn=troposphere.Ref(notif_param),
                    SourceType='db-cluster',
                )

        # DB Instance(s)
        for db_instance in rds_aurora.db_instances.values():
            logical_name = self.create_cfn_logical_id(db_instance.name)
            db_instance_dict = {
                'DBClusterIdentifier': troposphere.Ref(db_cluster_res),
                'DBInstanceClass': db_instance.get_value_or_default('db_instance_type'),
                'DBSubnetGroupName': troposphere.Ref(db_subnet_group_resource),
                'EnablePerformanceInsights': db_instance.get_value_or_default('enable_performance_insights'),
                'Engine': rds_aurora.engine,
                'PubliclyAccessible': db_instance.get_value_or_default('publicly_accessible'),
                'AllowMajorVersionUpgrade': db_instance.get_value_or_default('allow_major_version_upgrade'),
                'AutoMinorVersionUpgrade': db_instance.get_value_or_default('auto_minor_version_upgrade'),
            }
            enhanced_monitoring_interval = db_instance.get_value_or_default('enhanced_monitoring_interval_in_seconds')
            if enhanced_monitoring_interval != 0:
                db_instance_dict['MonitoringInterval'] = enhanced_monitoring_interval
                db_instance_dict['MonitoringRoleArn'] = troposphere.GetAtt(enhanced_monitoring_role_resource, "Arn")
            if db_instance.availability_zone != None:
                subnet_id_ref = f'{rds_aurora.segment}.az{db_instance.availability_zone}.availability_zone'
                db_instance_subnet_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'DBInstanceAZ{logical_name}',
                    description=f'Subnet where DB Instance {logical_name} is provisioned',
                    value=subnet_id_ref,
                )
                db_instance_dict['AvailabilityZone'] = troposphere.Ref(db_instance_subnet_param)

            # DB Parameter Group
            if default_instance.parameter_group == None and db_instance.parameter_group == None:
                dbparametergroup_resource = default_dbparametergroup_resource
            elif db_instance.parameter_group != None:
                # Use instance-specific DB Parameter Group
                dbparametergroup_resource = self.create_cfn_parameter(
                    name=f'DBParameterGroupName{logical_name}',
                    param_type='String',
                    description='DB Parameter Group Name',
                    value=db_instance.parameter_group + '.name',
                )
            else:
                # Use default DB Parameter Group
                dbparametergroup_resource = self.create_cfn_parameter(
                    name=f'DBParameterGroupName{logical_name}',
                    param_type='String',
                    description='DB Parameter Group Name',
                    value=default_instance.parameter_group + '.name',
                )
            db_instance_dict['DBParameterGroupName'] = troposphere.Ref(dbparametergroup_resource)

            db_instance_resource = troposphere.rds.DBInstance.from_dict(
                f'DBInstance{logical_name}',
                db_instance_dict
            )
            self.template.add_resource(db_instance_resource)

            # DB Event Notifications
            event_notifications = db_instance.get_value_or_default('event_notifications')
            if event_notifications != None:
                for group in event_notifications.groups:
                    notif_param = self.create_notification_param(group)
                    event_subscription_resource = troposphere.rds.EventSubscription(
                        title=self.create_cfn_logical_id(f"DBEventSubscription{logical_name}{group}"),
                        template=self.template,
                        EventCategories=event_notifications.event_categories,
                        SourceIds=[troposphere.Ref(db_instance_resource)],
                        SnsTopicArn=troposphere.Ref(notif_param),
                        SourceType='db-instance',
                    )

            # DB Instance Outputs
            self.create_output(
                title=f'DBInstanceName{logical_name}',
                description=f'DB Instance Name for {logical_name}',
                value=troposphere.Ref(db_instance_resource),
                ref=db_instance.paco_ref_parts + ".name",
            )

        # DB Cluster Outputs
        self.create_output(
            title='DBClusterName',
            description='DB Cluster Name',
            value=troposphere.Ref(db_cluster_res),
            ref=self.resource.paco_ref_parts + ".name",
        )
        self.create_output(
            title='ClusterEndpointAddress',
            description='Cluster Endpoint Address',
            value=troposphere.GetAtt(db_cluster_res, 'Endpoint.Address'),
            ref=self.resource.paco_ref_parts + ".endpoint.address",
        )
        self.create_output(
            title='ClusterEndpointPort',
            description='Cluster Endpoint Port',
            value=troposphere.GetAtt(db_cluster_res, 'Endpoint.Port'),
            ref=self.resource.paco_ref_parts + ".endpoint.port",
        )
        self.create_output(
            title='ClusterReadEndpointAddress',
            description='Cluster ReadEndpoint Address',
            value=troposphere.GetAtt(db_cluster_res, 'ReadEndpoint.Address'),
            ref=self.resource.paco_ref_parts + ".readendpoint.address",
        )

        # DNS - Route53 Record Set
        if rds_aurora.is_dns_enabled() == True:
            route53_ctl = self.paco_ctx.get_controller('route53')
            for dns in rds_aurora.dns:
                route53_ctl.add_record_set(
                    self.account_ctx,
                    self.aws_region,
                    rds_aurora,
                    enabled=rds_aurora.is_enabled(),
                    dns=dns,
                    record_set_type='CNAME',
                    resource_records=[rds_aurora.paco_ref + '.endpoint.address'],
                    stack_group=self.stack.stack_group,
                )
            for read_dns in rds_aurora.read_dns:
                route53_ctl.add_record_set(
                    self.account_ctx,
                    self.aws_region,
                    rds_aurora,
                    enabled=rds_aurora.is_enabled(),
                    dns=read_dns,
                    record_set_type='CNAME',
                    resource_records=[rds_aurora.paco_ref + '.readendpoint.address'],
                    stack_group=self.stack.stack_group,
                )
示例#23
0
文件: rds.py 项目: dwtcourses/paco
    def __init__(self, stack, paco_ctx,):
        rds_config = stack.resource
        config_ref = rds_config.paco_ref_parts
        super().__init__(stack, paco_ctx)
        self.set_aws_name('RDS', self.resource_group_name, self.resource.name)
        self.init_template('RDS')
        template = self.template
        if not rds_config.is_enabled(): return

        rds_logical_id = 'PrimaryDBInstance'

        # DB Subnet Group
        db_subnet_id_list_param = self.create_cfn_parameter(
            param_type='List<AWS::EC2::Subnet::Id>',
            name='DBSubnetIdList',
            description='The list of subnet IDs where this database will be provisioned.',
            value=rds_config.segment+'.subnet_id_list',
        )
        db_subnet_group_res = troposphere.rds.DBSubnetGroup(
            title='DBSubnetGroup',
            template =template,
            DBSubnetGroupDescription=troposphere.Ref('AWS::StackName'),
            SubnetIds=troposphere.Ref(db_subnet_id_list_param),
        )

        # DB Parameter Group
        if rds_config.parameter_group == None:
            # No Parameter Group supplied, create one
            engine_major_version = '.'.join(rds_config.engine_version.split('.')[0:2])
            param_group_family = gen_vocabulary.rds_engine_versions[rds_config.engine][rds_config.engine_version]['param_group_family']
            dbparametergroup_ref = troposphere.rds.DBParameterGroup(
                "DBParameterGroup",
                template = template,
                Family=param_group_family,
                Description=troposphere.Ref('AWS::StackName')
            )
        else:
            # Use an existing Parameter Group
            dbparametergroup_ref = self.create_cfn_parameter(
                name='DBParameterGroupName',
                param_type='String',
                description='DB Parameter Group Name',
                value=rds_config.parameter_group + '.name',
            )

        # Option Group
        option_group_res = None
        if len(rds_config.option_configurations) > 0:
            option_group_dict = {
                'EngineName': rds_config.engine,
                'MajorEngineVersion': engine_major_version,
                'OptionGroupDescription': troposphere.Ref('AWS::StackName')
            }
            if len(rds_config.option_configurations) > 0:
                option_config_list = []
                for option_config in rds_config.option_configurations:
                    option_config_dict = {
                        'OptionName': option_config.option_name,
                    }
                    if len(option_config.option_settings) > 0:
                        option_config_dict['OptionSettings'] = []
                        for option_setting in option_config.option_settings:
                            option_setting_dict = {
                                'Name': option_setting.name,
                                'Value': option_setting.value
                            }
                            option_config_dict['OptionSettings'].append(option_setting_dict)
                    option_config_list.append(option_config_dict)
                option_group_dict['OptionConfigurations'] = option_config_list

            option_group_res = troposphere.rds.OptionGroup.from_dict(
                'OptionGroup',
                option_group_dict )
            template.add_resource(option_group_res)

        # RDS MultiAZ (Mysql, Postgresql)
        if schemas.IRDSMultiAZ.providedBy(rds_config):
            sg_param_ref_list = []
            for sg_ref in rds_config.security_groups:
                sg_hash = utils.md5sum(str_data=sg_ref)
                sg_param = self.create_cfn_parameter(
                    param_type='AWS::EC2::SecurityGroup::Id',
                    name=self.create_cfn_logical_id('SecurityGroup'+sg_hash),
                    description='VPC Security Group to attach to the RDS.',
                    value=sg_ref+'.id',
                )
                sg_param_ref_list.append(troposphere.Ref(sg_param))

            db_instance_dict = {
                'Engine': rds_config.engine,
                'EngineVersion': rds_config.engine_version,
                'DBInstanceIdentifier': troposphere.Ref('AWS::StackName'),
                'DBInstanceClass': rds_config.db_instance_type,
                'DBSubnetGroupName': troposphere.Ref(db_subnet_group_res),
                'DBParameterGroupName': troposphere.Ref(dbparametergroup_ref),
                'CopyTagsToSnapshot': True,
                'AllowMajorVersionUpgrade': rds_config.allow_major_version_upgrade,
                'AutoMinorVersionUpgrade': rds_config.auto_minor_version_upgrade,
                'MultiAZ': rds_config.multi_az,
                'AllocatedStorage': rds_config.storage_size_gb,
                'StorageType': rds_config.storage_type,
                'BackupRetentionPeriod': rds_config.backup_retention_period,
                'Port': rds_config.port,
                'PreferredBackupWindow': rds_config.backup_preferred_window,
                'PreferredMaintenanceWindow': rds_config.maintenance_preferred_window,
                'VPCSecurityGroups': sg_param_ref_list
            }

            # License Model
            if rds_config.license_model:
                db_instance_dict['LicenseModel'] = rds_config.license_model

            # Deletion Protection
            if rds_config.deletion_protection:
                db_instance_dict['DeletionProtection'] = rds_config.deletion_protection

            # CloudWatch Logs Exports
            if len(rds_config.cloudwatch_logs_exports) > 0:
                db_instance_dict['EnableCloudwatchLogsExports'] = rds_config.cloudwatch_logs_exports

            # Option Group
            if option_group_res != None:
                db_instance_dict['OptionGroupName'] = troposphere.Ref(option_group_res)

            # DB Snapshot Identifier
            if rds_config.db_snapshot_identifier == '' or rds_config.db_snapshot_identifier == None:
                db_snapshot_id_enabled = False
            else:
                db_snapshot_id_enabled = True
            if db_snapshot_id_enabled == True:
                db_instance_dict['DBSnapshotIdentifier'] = rds_config.db_snapshot_identifier

            # Encryption
            if rds_config.kms_key_id == '' or rds_config.kms_key_id == None:
                encryption_enabled = False
            else:
                encryption_enabled = True
            if db_snapshot_id_enabled == False:
                db_instance_dict['StorageEncrypted'] = encryption_enabled
                if encryption_enabled:
                    db_instance_dict['KmsKeyId'] = rds_config.kms_key_id

            # Username and Passsword
            if db_snapshot_id_enabled == False:
                db_instance_dict['MasterUsername'] = rds_config.master_username
                if rds_config.secrets_password:
                    # Password from Secrets Manager
                    sta_logical_id = 'SecretTargetAttachmentRDS'
                    secret_arn_param = self.create_cfn_parameter(
                        param_type='String',
                        name='RDSSecretARN',
                        description='The ARN for the secret for the RDS master password.',
                        value=rds_config.secrets_password + '.arn',
                    )
                    secret_target_attachment_resource = troposphere.secretsmanager.SecretTargetAttachment(
                        title=sta_logical_id,
                        SecretId=troposphere.Ref(secret_arn_param),
                        TargetId=troposphere.Ref(rds_logical_id),
                        TargetType='AWS::RDS::DBInstance'
                    )
                    template.add_resource(secret_target_attachment_resource)

                    db_instance_dict['MasterUserPassword'] = troposphere.Join(
                        '',
                        ['{{resolve:secretsmanager:', troposphere.Ref(secret_arn_param), ':SecretString:password}}' ]
                    )
                else:
                    master_password_param = self.create_cfn_parameter(
                        param_type='String',
                        name='MasterUserPassword',
                        description='The master user password.',
                        value=rds_config.master_user_password,
                        noecho=True,
                    )
                    db_instance_dict['MasterUserPassword'] = troposphere.Ref(master_password_param)

            db_instance_res = troposphere.rds.DBInstance.from_dict(
                rds_logical_id,
                db_instance_dict
            )
            template.add_resource(db_instance_res)

            # Outputs
            self.create_output(
                title='DBInstanceName',
                description='DB Instance Name',
                value=troposphere.Ref(db_instance_res),
                ref=config_ref + ".name",
            )
            self.create_output(
                title='RDSEndpointAddress',
                description='RDS Endpoint URL',
                value=troposphere.GetAtt(db_instance_res, 'Endpoint.Address'),
                ref=config_ref + ".endpoint.address",
            )

            # Legacy Route53 Record Set
            if self.paco_ctx.legacy_flag('route53_record_set_2019_10_16') == True:
                if rds_config.is_dns_enabled() == True:
                    for dns_config in rds_config.dns_config:
                        dns_hash = utils.md5sum(str_data=(rds_config.hosted_zone+rds_config.domain_name))
                        primary_hosted_zone_id_param = self.create_cfn_parameter(
                            param_type='String',
                            name='DNSHostedZoneId'+dns_hash,
                            description='The hosted zone id to create the Route53 record set.',
                            value=rds_config.primary_hosted_zone+'.id',
                        )
                        record_set_res = troposphere.route53.RecordSetType(
                            title = 'RecordSet'+dns_hash,
                            template = template,
                            Comment = 'RDS Primary DNS',
                            HostedZoneId = troposphere.Ref(primary_hosted_zone_id_param),
                            Name = rds_config.primary_domain_name,
                            Type = 'CNAME',
                            TTL = dns_config.ttl,
                            ResourceRecords = [ troposphere.GetAtt(db_instance_res, 'Endpoint.Address')]
                        )
                        record_set_res.DependsOn = db_instance_res

        # DNS - Route53 Record Set
        if self.paco_ctx.legacy_flag('route53_record_set_2019_10_16') == False:
            if rds_config.is_dns_enabled() == True:
                route53_ctl = self.paco_ctx.get_controller('route53')
                for dns_config in rds_config.dns:
                    route53_ctl.add_record_set(
                        self.account_ctx,
                        self.aws_region,
                        rds_config,
                        enabled=rds_config.is_enabled(),
                        dns=dns_config,
                        record_set_type='CNAME',
                        resource_records=['paco.ref ' + config_ref + '.endpoint.address'],
                        stack_group=self.stack.stack_group,
                        config_ref=rds_config.paco_ref_parts + '.dns'
                    )
示例#24
0
def create_policy_statments(policies,
                            s3bucket_arn_param=None,
                            s3bucket_arn=None):
    policy_statements = []
    for policy_statement in policies:
        statement_dict = {
            'Effect':
            policy_statement.effect,
            'Action':
            [Action(*action.split(':')) for action in policy_statement.action],
        }

        # Sid
        if policy_statement.sid != None and len(policy_statement.sid) > 0:
            statement_dict['Sid'] = policy_statement.sid

        # Principal
        if policy_statement.principal != None and len(
                policy_statement.principal) > 0:
            # ToDo: awacs only allows one type of Principal ... is there a use-case where
            # multiple principal types are needed?
            for key, value in policy_statement.principal.items():
                statement_dict['Principal'] = Principal(key, value)
        elif policy_statement.aws != None and len(policy_statement.aws) > 0:
            statement_dict['Principal'] = Principal('AWS',
                                                    policy_statement.aws)

        # Condition
        if policy_statement.condition != {}:
            conditions = []
            for condition_key, condition_value in policy_statement.condition.items(
            ):
                # Conditions can be simple:
                #   StringEquals
                # Or prefixed with ForAnyValue or ForAllValues
                #   ForAnyValue:StringEquals
                condition_key = condition_key.replace(':', '')
                condition_class = globals()[condition_key]
                conditions.append(condition_class(condition_value))

            statement_dict['Condition'] = Condition(conditions)

        # Resource
        # S3BucketPolicy uses Parameters
        if s3bucket_arn_param != None:
            if policy_statement.resource_suffix and len(
                    policy_statement.resource_suffix) > 0:
                statement_dict['Resource'] = []
                for res_suffix in policy_statement.resource_suffix:
                    if res_suffix == '':
                        statement_dict['Resource'].append(
                            troposphere.Ref(s3bucket_arn_param))
                    else:
                        statement_dict['Resource'].append(
                            troposphere.Join('', [
                                troposphere.Ref(s3bucket_arn_param), res_suffix
                            ]))
            else:
                statement_dict['Resource'] = [
                    troposphere.Ref(s3bucket_arn_param)
                ]
        # S3Bucket uses embdedded strings
        else:
            if policy_statement.resource_suffix and len(
                    policy_statement.resource_suffix) > 0:
                statement_dict['Resource'] = [
                    s3bucket_arn + res_suffix
                    for res_suffix in policy_statement.resource_suffix
                ]
            else:
                statement_dict['Resource'] = [s3bucket_arn]

        policy_statements.append(Statement(**statement_dict))
    return policy_statements
示例#25
0
 def get_bucket_arn(self):
     bucket = self.settings.get('bucket')
     if bucket.startswith('arn:aws:'):
         return bucket
     return troposphere.Join("", ["arn:aws:s3:::", bucket])
    def create_template(self):

        t = self.template
        t.add_description("Acceptance Tests for cumulus pipelines")

        instance = self.name + self.context.environment['env']

        # TODO: give to builder
        the_chain = chain.Chain()
        # bucket becomes: cumulus-acceptance-tests-123123-namespace
        pipeline_bucket_name = troposphere.Join('', [
            self.context.namespace, "-",
            troposphere.Ref("AWS::AccountId"), "-", "automatedtests"
        ])

        the_chain.add(
            pipeline.Pipeline(
                name=self.name,
                bucket_name=pipeline_bucket_name,
            ))

        source_stage_name = "SourceStage"
        deploy_stage_name = "DeployStage"
        service_artifact = "ServiceArtifact"

        the_chain.add(
            pipeline_stage.PipelineStage(stage_name=source_stage_name))

        the_chain.add(
            pipeline_source_action.PipelineSourceAction(
                action_name="MicroserviceSource",
                output_artifact_name=service_artifact,
                s3_bucket_name=pipeline_bucket_name,
                s3_object_key="artifact.tar.gz"))

        the_chain.add(
            pipeline_stage.PipelineStage(stage_name=deploy_stage_name, ), )

        inline_ls_url_spec = """version: 0.2
phases:
  build:
    commands:
      - ls -lah
      - env
      - curl $URL
"""

        test_env = troposphere.codebuild.Environment(
            ComputeType='BUILD_GENERAL1_SMALL',
            Image='aws/codebuild/golang:1.10',
            Type='LINUX_CONTAINER',
            EnvironmentVariables=[{
                'Name': 'URL',
                'Value': "https://google.ca"
            }],
        )

        deploy_test = code_build_action.CodeBuildAction(
            action_name="DeployMyStuff",
            stage_name_to_add=deploy_stage_name,
            input_artifact_name=service_artifact,
            environment=test_env,
            buildspec=inline_ls_url_spec,
        )

        the_chain.add(deploy_test)

        lambda1 = lambda_action.LambdaAction(
            action_name="TriggerLambda",
            input_artifact_name=service_artifact,  # TODO make optional ?
            stage_name_to_add=deploy_stage_name,
            function_name="bswift-mock-function-mock-createUser")

        the_chain.add(lambda1)

        # the_chain.add(code_build_action.CodeBuildAction(
        #     action_name="NotificationSmokeTest",
        #     stage_name_to_add=deploy_stage_name,
        #     input_artifact_name=service_artifact,
        #     environment=test_env,
        #     buildspec='buildspec_smoke_test.yml',
        # ))

        # TODO: integration tests don't confirm the below.. yet.  Do it.
        destroy_stage_name = "EchoAURL"
        the_chain.add(
            pipeline_stage.PipelineStage(stage_name=destroy_stage_name, ), )

        the_chain.add(
            ApprovalAction(action_name="ApproveDestruction",
                           stage_name_to_add=destroy_stage_name))

        the_chain.add(
            code_build_action.CodeBuildAction(
                action_name="DestroyService",
                stage_name_to_add=destroy_stage_name,
                input_artifact_name=service_artifact,
            ))

        chain_context = chaincontext.ChainContext(template=t,
                                                  instance_name=instance)

        the_chain.run(chain_context)
示例#27
0
文件: s3.py 项目: revolunet/gordon
 def get_bucket_arn(self):
     bucket_name = self.get_bucket_name()
     return troposphere.Join("", ["arn:aws:s3:::", bucket_name])
示例#28
0
    def __init__(self, stack, paco_ctx,):
        rds_config = stack.resource
        config_ref = rds_config.paco_ref_parts
        super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_IAM"])
        self.set_aws_name('RDS', self.resource_group_name, self.resource.name)
        self.init_template('RDS')
        template = self.template
        if not rds_config.is_enabled(): return

        rds_logical_id = 'PrimaryDBInstance'

        # DB Subnet Group
        db_subnet_id_list_param = self.create_cfn_parameter(
            param_type='List<AWS::EC2::Subnet::Id>',
            name='DBSubnetIdList',
            description='The list of subnet IDs where this database will be provisioned.',
            value=rds_config.segment+'.subnet_id_list',
        )
        db_subnet_group_res = troposphere.rds.DBSubnetGroup(
            title='DBSubnetGroup',
            template =template,
            DBSubnetGroupDescription=troposphere.Ref('AWS::StackName'),
            SubnetIds=troposphere.Ref(db_subnet_id_list_param),
        )

        # DB Parameter Group
        engine_major_version = None
        if rds_config.parameter_group == None:
            # No Parameter Group supplied, create one
            engine_major_version = '.'.join(rds_config.engine_version.split('.')[0:2])
            param_group_family = gen_vocabulary.rds_engine_versions[rds_config.engine][rds_config.engine_version]['param_group_family']
            dbparametergroup_ref = troposphere.rds.DBParameterGroup(
                "DBParameterGroup",
                template = template,
                Family=param_group_family,
                Description=troposphere.Ref('AWS::StackName')
            )
        else:
            # Use an existing Parameter Group
            dbparametergroup_ref = self.create_cfn_parameter(
                name='DBParameterGroupName',
                param_type='String',
                description='DB Parameter Group Name',
                value=rds_config.parameter_group + '.name',
            )

        # Option Group
        option_group_res = None
        if len(rds_config.option_configurations) > 0 or (hasattr(rds_config, 'backup_restore_bucket') and rds_config.backup_restore_bucket != None):
            option_group_dict = {
                'EngineName': rds_config.engine,
                'MajorEngineVersion': engine_major_version,
                'OptionGroupDescription': troposphere.Ref('AWS::StackName')
            }
            option_config_list = []
            if len(rds_config.option_configurations) > 0:
                for option_config in rds_config.option_configurations:
                    option_config_dict = {
                        'OptionName': option_config.option_name,
                    }
                    if len(option_config.option_settings) > 0:
                        option_config_dict['OptionSettings'] = []
                        idx = 0
                        for option_setting in option_config.option_settings:
                            option_value = option_setting.value
                            if references.is_ref(option_setting.value):
                                # Use an existing Parameter Group
                                option_setting_value_param = self.create_cfn_parameter(
                                    name=f'OptionsGroupValue{idx}',
                                    param_type='String',
                                    description=f'DB Option Settings Value {idx}',
                                    value=option_setting.value
                                )
                                option_value = troposphere.Ref(option_setting_value_param)

                            option_setting_dict = {
                                'Name': option_setting.name,
                                'Value': option_value
                            }
                            option_config_dict['OptionSettings'].append(option_setting_dict)
                    option_config_list.append(option_config_dict)
            if hasattr(rds_config, 'backup_restore_bucket') and rds_config.backup_restore_bucket != None:
                option_config_dict = {
                    'OptionName': 'SQLSERVER_BACKUP_RESTORE',
                    'OptionSettings': []
                }
                # S3 Bucket Arn Param
                backup_restore_bucket_arn_param = self.create_cfn_parameter(
                    name='SQLServerBackupRestoreBucketArn',
                    param_type='String',
                    description=f'DB Option Setting SQLServer Backup Restore Bucket ARN',
                    value=f'{rds_config.backup_restore_bucket}.arn'
                )
                # Create Role for SQLServer Bucket
                sqlserver_backup_restore_role = troposphere.iam.Role(
                    title='SQLServerBackupRestoreRole',
                    template=self.template,
                    AssumeRolePolicyDocument=PolicyDocument(
                        Statement=[
                            Statement(
                                Effect=Allow,
                                Action=[Action("sts", "AssumeRole")],
                                Principal=Principal("Service", "rds.amazonaws.com")
                            )
                        ]
                    ),
                    Policies=[
                        troposphere.iam.Policy(
                            PolicyName="S3BucketAccess",
                            PolicyDocument=Policy(
                                Version='2012-10-17',
                                Statement=[
                                    Statement(
                                        Effect=Allow,
                                        Action=[
                                            awacs.s3.ListBucket,
                                            awacs.s3.GetBucketLocation
                                        ],
                                        Resource=[troposphere.Ref(backup_restore_bucket_arn_param)],
                                    ),
                                    Statement(
                                        Effect=Allow,
                                        Action=[
                                            Action('s3', 'GetObjectMetaData'),
                                            awacs.s3.GetObject,
                                            awacs.s3.PutObject,
                                            awacs.s3.ListMultipartUploadParts,
                                            awacs.s3.AbortMultipartUpload
                                        ],
                                        Resource=[troposphere.Sub("${SQLServerBackupRestoreBucketArn}/*")]
                                    )
                                ]
                            )
                        )
                    ],
                    Path="/",
                )
                option_value = troposphere.GetAtt(sqlserver_backup_restore_role, 'Arn')

                option_setting_dict = {
                    'Name': 'IAM_ROLE_ARN',
                    'Value': option_value
                }
                option_config_dict['OptionSettings'].append(option_setting_dict)
                option_config_list.append(option_config_dict)
            option_group_dict['OptionConfigurations'] = option_config_list

            option_group_res = troposphere.rds.OptionGroup.from_dict(
                'OptionGroup',
                option_group_dict )
            template.add_resource(option_group_res)

        # RDS MultiAZ (Mysql, Postgresql)
        sg_param_ref_list = []
        for sg_ref in rds_config.security_groups:
            sg_hash = utils.md5sum(str_data=sg_ref)
            sg_param = self.create_cfn_parameter(
                param_type='AWS::EC2::SecurityGroup::Id',
                name=self.create_cfn_logical_id('SecurityGroup'+sg_hash),
                description='VPC Security Group to attach to the RDS.',
                value=sg_ref+'.id',
            )
            sg_param_ref_list.append(troposphere.Ref(sg_param))

        db_instance_dict = {
            'Engine': rds_config.engine,
            'EngineVersion': rds_config.engine_version,
            'DBInstanceIdentifier': troposphere.Ref('AWS::StackName'),
            'DBInstanceClass': rds_config.db_instance_type,
            'DBSubnetGroupName': troposphere.Ref(db_subnet_group_res),
            'DBParameterGroupName': troposphere.Ref(dbparametergroup_ref),
            'CopyTagsToSnapshot': True,
            'AllowMajorVersionUpgrade': rds_config.allow_major_version_upgrade,
            'AutoMinorVersionUpgrade': rds_config.auto_minor_version_upgrade,
            'AllocatedStorage': rds_config.storage_size_gb,
            'StorageType': rds_config.storage_type,
            'BackupRetentionPeriod': rds_config.backup_retention_period,
            'Port': rds_config.port,
            'PreferredBackupWindow': rds_config.backup_preferred_window,
            'PreferredMaintenanceWindow': rds_config.maintenance_preferred_window,
            'VPCSecurityGroups': sg_param_ref_list
        }

        if schemas.IRDSMultiAZ.providedBy(rds_config):
            db_instance_dict['MultiAZ'] = rds_config.multi_az


        # License Model
        if rds_config.license_model:
            db_instance_dict['LicenseModel'] = rds_config.license_model

        # Deletion Protection
        if rds_config.deletion_protection:
            db_instance_dict['DeletionProtection'] = rds_config.deletion_protection

        # CloudWatch Logs Exports
        if len(rds_config.cloudwatch_logs_exports) > 0:
            db_instance_dict['EnableCloudwatchLogsExports'] = rds_config.cloudwatch_logs_exports

        # Option Group
        if option_group_res != None:
            db_instance_dict['OptionGroupName'] = troposphere.Ref(option_group_res)

        # DB Snapshot Identifier
        if rds_config.db_snapshot_identifier == '' or rds_config.db_snapshot_identifier == None:
            db_snapshot_id_enabled = False
        else:
            db_snapshot_id_enabled = True
        if db_snapshot_id_enabled == True:
            db_instance_dict['DBSnapshotIdentifier'] = rds_config.db_snapshot_identifier
            # To restore an existing DB from a Snapshot, RDS will need to replace the RDS
            # resource, in which case the DBInstanceIdentifier name CAN NOT be set
            # del db_instance_dict['DBInstanceIdentifier']

        # Username and Passsword
        if db_snapshot_id_enabled == False:
            # Encryption
            if rds_config.storage_encrypted == True:
                db_instance_dict['StorageEncrypted'] = True
            if rds_config.kms_key_id and rds_config.kms_key_id != '':
                db_instance_dict['KmsKeyId'] = rds_config.kms_key_id

            # Username & Password
            db_instance_dict['MasterUsername'] = rds_config.master_username
            if rds_config.secrets_password:
                # Password from Secrets Manager
                sta_logical_id = 'SecretTargetAttachmentRDS'
                secret_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name='RDSSecretARN',
                    description='The ARN for the secret for the RDS master password.',
                    value=rds_config.secrets_password + '.arn',
                )
                secret_target_attachment_resource = troposphere.secretsmanager.SecretTargetAttachment(
                    title=sta_logical_id,
                    SecretId=troposphere.Ref(secret_arn_param),
                    TargetId=troposphere.Ref(rds_logical_id),
                    TargetType='AWS::RDS::DBInstance'
                )
                template.add_resource(secret_target_attachment_resource)

                db_instance_dict['MasterUserPassword'] = troposphere.Join(
                    '',
                    ['{{resolve:secretsmanager:', troposphere.Ref(secret_arn_param), ':SecretString:password}}' ]
                )
            else:
                master_password_param = self.create_cfn_parameter(
                    param_type='String',
                    name='MasterUserPassword',
                    description='The master user password.',
                    value=rds_config.master_user_password,
                    noecho=True,
                )
                db_instance_dict['MasterUserPassword'] = troposphere.Ref(master_password_param)

        db_instance_res = troposphere.rds.DBInstance.from_dict(
            rds_logical_id,
            db_instance_dict
        )
        template.add_resource(db_instance_res)

        # Outputs
        self.create_output(
            title='DBInstanceName',
            description='DB Instance Name',
            value=troposphere.Ref(db_instance_res),
            ref=config_ref + ".name",
        )
        self.create_output(
            title='RDSEndpointAddress',
            description='RDS Endpoint URL',
            value=troposphere.GetAtt(db_instance_res, 'Endpoint.Address'),
            ref=config_ref + ".endpoint.address",
        )

        # Legacy Route53 Record Set
        if self.paco_ctx.legacy_flag('route53_record_set_2019_10_16') == True:
            if rds_config.is_dns_enabled() == True:
                for dns_config in rds_config.dns_config:
                    dns_hash = utils.md5sum(str_data=(rds_config.hosted_zone+rds_config.domain_name))
                    primary_hosted_zone_id_param = self.create_cfn_parameter(
                        param_type='String',
                        name='DNSHostedZoneId'+dns_hash,
                        description='The hosted zone id to create the Route53 record set.',
                        value=rds_config.primary_hosted_zone+'.id',
                    )
                    record_set_res = troposphere.route53.RecordSetType(
                        title = 'RecordSet'+dns_hash,
                        template = template,
                        Comment = 'RDS Primary DNS',
                        HostedZoneId = troposphere.Ref(primary_hosted_zone_id_param),
                        Name = rds_config.primary_domain_name,
                        Type = 'CNAME',
                        TTL = dns_config.ttl,
                        ResourceRecords = [ troposphere.GetAtt(db_instance_res, 'Endpoint.Address')]
                    )
                    record_set_res.DependsOn = db_instance_res

        # DNS - Route53 Record Set
        if self.paco_ctx.legacy_flag('route53_record_set_2019_10_16') == False:
            if rds_config.is_dns_enabled() == True:
                route53_ctl = self.paco_ctx.get_controller('route53')
                for dns_config in rds_config.dns:
                    route53_ctl.add_record_set(
                        self.account_ctx,
                        self.aws_region,
                        rds_config,
                        enabled=rds_config.is_enabled(),
                        dns=dns_config,
                        record_set_type='CNAME',
                        resource_records=['paco.ref ' + config_ref + '.endpoint.address'],
                        stack_group=self.stack.stack_group,
                        async_stack_provision=True,
                        config_ref=rds_config.paco_ref_parts + '.dns'
                    )
示例#29
0
    def __init__(
        self,
        stack,
        paco_ctx,
    ):
        super().__init__(
            stack,
            paco_ctx,
            iam_capabilities=["CAPABILITY_NAMED_IAM"],
        )
        account_ctx = stack.account_ctx
        aws_region = stack.aws_region
        self.set_aws_name('Lambda', self.resource_group_name, self.resource_name)
        awslambda = self.awslambda = self.stack.resource
        self.init_template('Lambda Function')

        # if not enabled finish with only empty placeholder
        if not awslambda.is_enabled(): return

        # Parameters
        sdb_cache_param = self.create_cfn_parameter(
            name='EnableSDBCache',
            param_type='String',
            description='Boolean indicating whether an SDB Domain will be created to be used as a cache.',
            value=awslambda.sdb_cache
        )
        function_description_param = self.create_cfn_parameter(
            name='FunctionDescription',
            param_type='String',
            description='A description of the Lamdba Function.',
            value=awslambda.description
        )
        handler_param = self.create_cfn_parameter(
            name='Handler',
            param_type='String',
            description='The name of the function to call upon execution.',
            value=awslambda.handler
        )
        runtime_param = self.create_cfn_parameter(
            name='Runtime',
            param_type='String',
            description='The name of the runtime language.',
            value=awslambda.runtime
        )
        role_arn_param = self.create_cfn_parameter(
            name='RoleArn',
            param_type='String',
            description='The execution role for the Lambda Function.',
            value=awslambda.iam_role.get_arn()
        )
        role_name_param = self.create_cfn_parameter(
            name='RoleName',
            param_type='String',
            description='The execution role name for the Lambda Function.',
            value=awslambda.iam_role.resolve_ref_obj.role_name
        )
        memory_size_param = self.create_cfn_parameter(
            name='MemorySize',
            param_type='Number',
            description="The amount of memory that your function has access to. Increasing the function's" + \
            " memory also increases its CPU allocation. The default value is 128 MB. The value must be a multiple of 64 MB.",
            value=awslambda.memory_size
        )
        reserved_conc_exec_param = self.create_cfn_parameter(
            name='ReservedConcurrentExecutions',
            param_type='Number',
            description='The number of simultaneous executions to reserve for the function.',
            value=awslambda.reserved_concurrent_executions
        )
        timeout_param = self.create_cfn_parameter(
            name='Timeout',
            param_type='Number',
            description='The amount of time that Lambda allows a function to run before stopping it. ',
            value=awslambda.timeout
        )
        layers_param = self.create_cfn_parameter(
            name='Layers',
            param_type='CommaDelimitedList',
            description='List of up to 5 Lambda Layer ARNs.',
            value=','.join(awslambda.layers)
        )

        # create the Lambda resource
        cfn_export_dict = {
            'Description': troposphere.Ref(function_description_param),
            'Handler': troposphere.Ref(handler_param),
            'MemorySize': troposphere.Ref(memory_size_param),
            'Runtime': troposphere.Ref(runtime_param),
            'Role': troposphere.Ref(role_arn_param),
            'Timeout': troposphere.Ref(timeout_param),
        }
        if awslambda.reserved_concurrent_executions:
            cfn_export_dict['ReservedConcurrentExecutions'] = troposphere.Ref(reserved_conc_exec_param),

        if len(awslambda.layers) > 0:
            cfn_export_dict['Layers'] = troposphere.Ref(layers_param),

        # Lambda VPC
        if awslambda.vpc_config != None:
            vpc_security_group = self.create_cfn_ref_list_param(
                name='VpcSecurityGroupIdList',
                param_type='List<AWS::EC2::SecurityGroup::Id>',
                description='VPC Security Group Id List',
                value=awslambda.vpc_config.security_groups,
                ref_attribute='id',
            )
            # Segment SubnetList is a Segment stack Output based on availability zones
            segment_ref = awslambda.vpc_config.segments[0] + '.subnet_id_list'
            subnet_list_param = self.create_cfn_parameter(
                name='VpcSubnetIdList',
                param_type='List<AWS::EC2::Subnet::Id>',
                description='VPC Subnet Id List',
                value=segment_ref
            )
            cfn_export_dict['VpcConfig'] = {
                'SecurityGroupIds': troposphere.Ref(vpc_security_group),
                'SubnetIds': troposphere.Ref(subnet_list_param),
            }

        # Code object: S3 Bucket, inline ZipFile or deploy artifact?
        if awslambda.code.s3_bucket:
            if awslambda.code.s3_bucket.startswith('paco.ref '):
                value = awslambda.code.s3_bucket + ".name"
            else:
                value = awslambda.code.s3_bucket
            s3bucket_param = self.create_cfn_parameter(
                name='CodeS3Bucket',
                description="An Amazon S3 bucket in the same AWS Region as your function. The bucket can be in a different AWS account.",
                param_type='String',
                value=value
            )
            s3key_param = self.create_cfn_parameter(
                name='CodeS3Key',
                description="The Amazon S3 key of the deployment package.",
                param_type='String',
                value=awslambda.code.s3_key
            )
            cfn_export_dict['Code'] = {
                'S3Bucket': troposphere.Ref(s3bucket_param),
                'S3Key': troposphere.Ref(s3key_param),
            }
        else:
            zip_path = Path(awslambda.code.zipfile)
            if zip_path.is_file():
                cfn_export_dict['Code'] = {
                    'ZipFile': zip_path.read_text()
                }
            elif zip_path.is_dir():
                # get S3Bucket/S3Key or if it does not exist, it will create the bucket and artifact
                # and then upload the artifact
                bucket_name, artifact_name = init_lambda_code(
                    self.paco_ctx.paco_buckets,
                    self.stack.resource,
                    awslambda.code.zipfile,
                    self.stack.account_ctx,
                    self.stack.aws_region,
                )
                s3bucket_param = self.create_cfn_parameter(
                    name='CodeS3Bucket',
                    description="The Paco S3 Bucket for configuration",
                    param_type='String',
                    value=bucket_name
                )
                s3key_param = self.create_cfn_parameter(
                    name='CodeS3Key',
                    description="The Lambda code artifact S3 Key.",
                    param_type='String',
                    value=artifact_name
                )
                cfn_export_dict['Code'] = {
                    'S3Bucket': troposphere.Ref(s3bucket_param),
                    'S3Key': troposphere.Ref(s3key_param),
                }

        # Environment variables
        var_export = {}
        if awslambda.environment != None and awslambda.environment.variables != None:
            for var in awslambda.environment.variables:
                name = var.key.replace('_','')
                env_param = self.create_cfn_parameter(
                    name='EnvVar{}'.format(name),
                    param_type='String',
                    description='Env var for {}'.format(name),
                    value=var.value,
                )
                var_export[var.key] = troposphere.Ref(env_param)
            if awslambda.sdb_cache == True:
                var_export['SDB_CACHE_DOMAIN'] = troposphere.Ref('LambdaSDBCacheDomain')
            if len(awslambda.log_group_names) > 0:
                # Add PACO_LOG_GROUPS Environment Variable
                paco_log_groups = [
                    prefixed_name(awslambda, loggroup_name, self.paco_ctx.legacy_flag)
                    for loggroup_name in awslambda.log_group_names
                ]
                paco_log_groups_param = self.create_cfn_parameter(
                    name='EnvVariablePacoLogGroups',
                    param_type='String',
                    description='Env var for Paco Log Groups',
                    value=','.join(paco_log_groups),
                )
                var_export['PACO_LOG_GROUPS'] = troposphere.Ref(paco_log_groups_param)
        cfn_export_dict['Environment'] = { 'Variables': var_export }

        # Lambda resource
        self.awslambda_resource = troposphere.awslambda.Function.from_dict(
            'Function',
            cfn_export_dict
        )
        self.template.add_resource(self.awslambda_resource)

        # SDB Cache with SDB Domain and SDB Domain Policy resources
        if awslambda.sdb_cache == True:
            sdb_domain_resource = troposphere.sdb.Domain(
                title='LambdaSDBCacheDomain',
                template=self.template,
                Description="Lambda Function Domain"
            )
            sdb_policy = troposphere.iam.Policy(
                title='LambdaSDBCacheDomainPolicy',
                template=self.template,
                PolicyName='SDBDomain',
                PolicyDocument=Policy(
                    Version='2012-10-17',
                    Statement=[
                        Statement(
                            Effect=Allow,
                            Action=[Action("sdb","*")],
                            Resource=[
                                troposphere.Sub(
                                    'arn:aws:sdb:${AWS::Region}:${AWS::AccountId}:domain/${DomainName}',
                                    DomainName=troposphere.Ref('LambdaSDBCacheDomain')
                                )
                            ],
                        )
                    ],
                    Roles=troposphere.Ref(role_arn_param)
                )
            )
            sdb_policy.DependsOn = sdb_domain_resource
            self.awslambda_resource.DependsOn = sdb_domain_resource

        # Permissions
        # SNS Topic Lambda permissions and subscription
        idx = 1
        for sns_topic_ref in awslambda.sns_topics:
            # SNS Topic Arn parameters
            param_name = 'SNSTopicArn%d' % idx
            self.create_cfn_parameter(
                name=param_name,
                param_type='String',
                description='An SNS Topic ARN to grant permission to.',
                value=sns_topic_ref + '.arn'
            )

            # Lambda permission
            troposphere.awslambda.Permission(
                title=param_name + 'Permission',
                template=self.template,
                Action="lambda:InvokeFunction",
                FunctionName=troposphere.GetAtt(self.awslambda_resource, 'Arn'),
                Principal='sns.amazonaws.com',
                SourceArn=troposphere.Ref(param_name),
            )

            # SNS Topic subscription
            sns_topic = get_model_obj_from_ref(sns_topic_ref, self.paco_ctx.project)
            troposphere.sns.SubscriptionResource(
                title=param_name + 'Subscription',
                template=self.template,
                Endpoint=troposphere.GetAtt(self.awslambda_resource, 'Arn'),
                Protocol='lambda',
                TopicArn=troposphere.Ref(param_name),
                Region=sns_topic.region_name
            )
            idx += 1


        # Lambda permissions for connected Paco resources

        app = get_parent_by_interface(awslambda, schemas.IApplication)
        for obj in get_all_nodes(app):
            # S3 Bucket notification permission(s)
            if schemas.IS3Bucket.providedBy(obj):
                seen = {}
                if hasattr(obj, 'notifications'):
                    if hasattr(obj.notifications, 'lambdas'):
                        for lambda_notif in obj.notifications.lambdas:
                            if lambda_notif.function == awslambda.paco_ref:
                                # yes, this Lambda gets notification from this S3Bucket
                                group = get_parent_by_interface(obj, schemas.IResourceGroup)
                                s3_logical_name = self.gen_cf_logical_name(group.name + obj.name, '_')
                                if s3_logical_name not in seen:
                                    troposphere.awslambda.Permission(
                                        title='S3Bucket' + s3_logical_name,
                                        template=self.template,
                                        Action="lambda:InvokeFunction",
                                        FunctionName=troposphere.GetAtt(self.awslambda_resource, 'Arn'),
                                        Principal='s3.amazonaws.com',
                                        SourceArn='arn:aws:s3:::' + obj.get_bucket_name(),
                                    )
                                    seen[s3_logical_name] = True

            # Events Rule permission(s)
            if schemas.IEventsRule.providedBy(obj):
                seen = {}
                for target in obj.targets:
                    target_ref = Reference(target.target)
                    target_ref.set_account_name(account_ctx.get_name())
                    target_ref.set_region(aws_region)
                    lambda_ref = Reference(awslambda.paco_ref)

                    if target_ref.raw == lambda_ref.raw:
                        # yes, the Events Rule has a Target that is this Lambda
                        group = get_parent_by_interface(obj, schemas.IResourceGroup)
                        eventsrule_logical_name = self.gen_cf_logical_name(group.name + obj.name, '_')
                        if eventsrule_logical_name not in seen:
                            rule_name = create_event_rule_name(obj)
                            # rule_name = self.create_cfn_logical_id("EventsRule" + obj.paco_ref)
                            # rule_name = hash_smaller(rule_name, 64)
                            source_arn = 'arn:aws:events:{}:{}:rule/{}'.format(
                                aws_region,
                                account_ctx.id,
                                rule_name
                            )
                            troposphere.awslambda.Permission(
                                title='EventsRule' + eventsrule_logical_name,
                                template=self.template,
                                Action="lambda:InvokeFunction",
                                FunctionName=troposphere.GetAtt(self.awslambda_resource, 'Arn'),
                                Principal='events.amazonaws.com',
                                SourceArn=source_arn,
                            )
                            seen[eventsrule_logical_name] = True

            # IoT Analytics permission(s)
            if schemas.IIoTAnalyticsPipeline.providedBy(obj):
                seen = {}
                for activity in obj.pipeline_activities.values():
                    if activity.activity_type == 'lambda':
                        target_ref = Reference(activity.function)
                        target_ref.set_account_name(account_ctx.get_name())
                        target_ref.set_region(aws_region)
                        lambda_ref = Reference(awslambda.paco_ref)
                        if target_ref.raw == lambda_ref.raw:
                            # yes, the IoT Analytics Lambda Activity has a ref to this Lambda
                            group = get_parent_by_interface(obj, schemas.IResourceGroup)
                            iotap_logical_name = self.gen_cf_logical_name(group.name + obj.name, '_')
                            if iotap_logical_name not in seen:
                                rule_name = create_event_rule_name(obj)
                                troposphere.awslambda.Permission(
                                    title='IoTAnalyticsPipeline' + iotap_logical_name,
                                    template=self.template,
                                    Action="lambda:InvokeFunction",
                                    FunctionName=troposphere.GetAtt(self.awslambda_resource, 'Arn'),
                                    Principal='iotanalytics.amazonaws.com',
                                )
                                seen[iotap_logical_name] = True

        # Log group(s)
        loggroup_function_name = troposphere.Join(
            '', [
                '/aws/lambda/',
                troposphere.Select(
                    6, troposphere.Split(':', troposphere.GetAtt(self.awslambda_resource, 'Arn'))
                )
            ]
        )
        loggroup_resources = []
        loggroup_resources.append(
            self.add_log_group(loggroup_function_name, 'lambda')
        )
        if len(awslambda.log_group_names) > 0:
            # Additional App-specific LogGroups
            for loggroup_name in awslambda.log_group_names:
                # Add LogGroup to the template
                prefixed_loggroup_name = prefixed_name(awslambda, loggroup_name, self.paco_ctx.legacy_flag)
                loggroup_resources.append(
                    self.add_log_group(prefixed_loggroup_name)
                )

        # LogGroup permissions
        log_group_arns = [
            troposphere.Join(':', [
                f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group',
                loggroup_function_name,
                '*'
            ])
        ]
        log_stream_arns = [
            troposphere.Join(':', [
                f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group',
                loggroup_function_name,
                'log-stream',
                '*'
            ])
        ]
        for loggroup_name in awslambda.log_group_names:
            prefixed_loggroup_name = prefixed_name(awslambda, loggroup_name, self.paco_ctx.legacy_flag)
            log_group_arns.append(
                f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group:{prefixed_loggroup_name}:*'
            )
            log_stream_arns.append(
                f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group:{prefixed_loggroup_name}:log-stream:*'
            )

        loggroup_policy_resource = troposphere.iam.ManagedPolicy(
            title='LogGroupManagedPolicy',
            PolicyDocument=Policy(
                Version='2012-10-17',
                Statement=[
                    Statement(
                        Sid='AllowLambdaModifyLogStreams',
                        Effect=Allow,
                        Action=[
                            Action("logs","CreateLogStream"),
                            Action("logs","DescribeLogStreams"),
                        ],
                        Resource=log_group_arns,
                    ),
                    Statement(
                        Sid='AllowLambdaPutLogEvents',
                        Effect=Allow,
                        Action=[
                            Action("logs","PutLogEvents"),
                        ],
                        Resource=log_stream_arns,
                    ),
                ],
            ),
            Roles=[troposphere.Ref(role_name_param)],
        )
        loggroup_policy_resource.DependsOn = loggroup_resources
        self.template.add_resource(loggroup_policy_resource)

        # Outputs
        self.create_output(
            title='FunctionName',
            value=troposphere.Ref(self.awslambda_resource),
            ref=awslambda.paco_ref_parts + '.name',
        )
        self.create_output(
            title='FunctionArn',
            value=troposphere.GetAtt(self.awslambda_resource, 'Arn'),
            ref=awslambda.paco_ref_parts + '.arn',
        )
示例#30
0
    def register_resources_template(self, template):

        deployment_resources = []
        api = RestApi(
            self.in_project_cf_name,
            Name=troposphere.Join("-", [self.name, troposphere.Ref('Stage')]),
            Description=self.settings.get('description', '')
        )
        template.add_resource(api)
        deployment_resources.append(api)

        invoke_lambda_role = troposphere.iam.Role(
            utils.valid_cloudformation_name(self.name, 'Role'),
            AssumeRolePolicyDocument={
                "Version": "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Principal": {
                        "Service": ["apigateway.amazonaws.com"]
                    },
                    "Action": ["sts:AssumeRole"]
                }]
            },
            Policies=[
                troposphere.iam.Policy(
                    PolicyName=utils.valid_cloudformation_name(self.name, 'Role', 'Policy'),
                    PolicyDocument={
                        "Version": "2012-10-17",
                        "Statement": [
                            {
                                "Effect": "Allow",
                                "Action": [
                                    "lambda:InvokeFunction"
                                ],
                                "Resource": [
                                    "*"
                                ]
                            }
                        ]
                    }
                )
            ]
        )

        template.add_resource(invoke_lambda_role)
        deployment_resources.append(invoke_lambda_role)

        deployment_dependencies = []
        for path, resource in six.iteritems(self.settings.get('resources', {})):
            resource_reference = self.get_or_create_resource(path, api, template)
            methods = resource['methods']

            if isinstance(methods, six.string_types):
                methods = [methods]

            if not isinstance(methods, dict):
                method_properties = copy.deepcopy(resource)
                method_properties.pop('methods', None)
                methods = dict([[method, method_properties] for method in methods])

            for method, configuration in six.iteritems(methods):
                method_name = [self.name]
                method_name.extend(path.split('/'))
                method_name.append(method)

                extra = {}
                if 'parameters' in configuration:
                    extra['RequestParameters'] = configuration['parameters']
                m = Method(
                    utils.valid_cloudformation_name(*method_name),
                    HttpMethod=method,
                    AuthorizationType=self.get_authorization_type(configuration),
                    ApiKeyRequired=self.get_api_key_required(configuration),
                    Integration=self.get_integration(configuration, invoke_lambda_role),
                    MethodResponses=self.get_method_responses(configuration),
                    ResourceId=resource_reference,
                    RestApiId=troposphere.Ref(api),
                    **extra
                )
                template.add_resource(m)
                deployment_dependencies.append(m.name)
                deployment_resources.append(m)

        deploy_hash = hashlib.sha1(six.text_type(uuid.uuid4()).encode('utf-8')).hexdigest()
        deploy = Deployment(
            utils.valid_cloudformation_name(self.name, "Deployment", deploy_hash[:8]),
            DependsOn=sorted(deployment_dependencies),
            StageName=troposphere.Ref('Stage'),
            RestApiId=troposphere.Ref(api)
        )

        template.add_resource(deploy)

        if self._get_true_false('cli-output', 't'):
            template.add_output([
                troposphere.Output(
                    utils.valid_cloudformation_name("Clioutput", self.in_project_name),
                    Value=troposphere.Join(
                        "",
                        [
                            "https://",
                            troposphere.Ref(api),
                            ".execute-api.",
                            troposphere.Ref(troposphere.AWS_REGION),
                            ".amazonaws.com/",
                            troposphere.Ref('Stage')
                        ]
                    ),
                )
            ])