Example #1
0
    def test_condition_equality(self):
        self.assertEqualWithHash(
            Condition(StringLike("s3:prefix", ["home/${aws:username}/*"])),
            Condition(StringLike("s3:prefix", ["home/${aws:username}/*"])))

        self.assertNotEqualWithHash(
            Condition(StringLike("s3:prefix", ["home/${aws:username}/*"])),
            Condition(StringLike("s3:prefix", ["other/${aws:username}/*"])))

        self.assertNotEqualWithHash(
            Condition(StringLike("s3:prefix", ["home/${aws:username}/*"])),
            Condition(StringEquals("s3:prefix", ["home/${aws:username}/*"])))
    def init_systemsmanagersession_permission(self, permission_config,
                                              assume_role_res):
        if 'ManagedPolicyArns' not in assume_role_res.properties.keys():
            assume_role_res.properties['ManagedPolicyArns'] = []

        resource_group_condition_list = []
        for resource in permission_config.resources:
            resource_ref = Reference(resource)
            # Initialize The network environments that we need access into
            resource_obj = resource_ref.get_model_obj(self.paco_ctx.project)
            if schemas.IResourceGroup.providedBy(resource_obj):
                resource_group_condition_list.append(
                    StringLike({
                        'ssm:resourceTag/Paco-Application-Group-Name':
                        resource_obj.name
                    }))

        if len(resource_group_condition_list) == 0:
            return

        statement_list = []
        statement_list.append(
            Statement(
                Sid='SessionManagerStartSession',
                Effect=Allow,
                Action=[
                    Action('ssm', 'StartSession'),
                ],
                Resource=[
                    'arn:aws:ec2:*:*:instance/*',
                    'arn:aws:ssm:*::document/AWS-StartPortForwardingSession'
                ],
                Condition=Condition(resource_group_condition_list)))
        statement_list.append(
            Statement(
                Sid='SessionManagerPortForward',
                Effect=Allow,
                Action=[
                    Action('ssm', 'StartSession'),
                ],
                Resource=[
                    'arn:aws:ssm:*::document/AWS-StartPortForwardingSession'
                ]))
        statement_list.append(
            Statement(Sid='SessionManagerTerminateSession',
                      Effect=Allow,
                      Action=[
                          Action('ssm', 'TerminateSession'),
                          Action('ssm', 'ResumeSession'),
                      ],
                      Resource=['arn:aws:ssm:*:*:session/${aws:username}-*']))
        managed_policy_res = troposphere.iam.ManagedPolicy(
            title=self.create_cfn_logical_id_join(["SystemsManagerSession"]),
            PolicyDocument=PolicyDocument(Version="2012-10-17",
                                          Statement=statement_list),
            Roles=[troposphere.Ref(assume_role_res)])
        self.template.add_resource(managed_policy_res)
Example #3
0
def kms_key_statements(key_arn, bucket_arn, bucket_prefix):
    s3_endpoint = Join('', ["s3.", REGION, "amazonaws.com"])
    return [
        Statement(Effect=Allow,
                  Action=[
                      awacs.kms.Decrypt,
                      awacs.kms.GenerateDataKey,
                  ],
                  Resource=[key_arn],
                  Condition=Condition([
                      StringEquals("kms:ViaService", s3_endpoint),
                      StringLike("kms:EncryptionContext:aws:s3:arn",
                                 Join('', [bucket_arn, bucket_prefix, "*"]))
                  ]))
    ]
Example #4
0
File: s3.py Project: somcsel/awacs
            s3.S3_ARN("*"),
        ],
    ),
    Statement(
        Action=[s3.ListBucket],
        Effect=Allow,
        Resource=[s3.S3_ARN("myBucket")],
        Condition=Condition(
            StringEquals({
                's3:prefix': ['', 'home/'],
                's3:delimiter': ['/'],
            }), ),
    ),
    Statement(
        Action=[s3.ListBucket],
        Effect=Allow,
        Resource=[s3.S3_ARN("myBucket")],
        Condition=Condition(StringLike("s3:prefix",
                                       ["home/${aws:username}/*"])),
    ),
    Statement(
        Action=[Action("s3", "*")],
        Effect=Allow,
        Resource=[
            s3.S3_ARN("myBucket/home/${aws:username}"),
            s3.S3_ARN("myBucket/home/${aws:username}/*"),
        ],
    ),
], )
print(pd.to_json())
Example #5
0
    def create_template(self):
        """Create template (main function called by Stacker)."""
        template = self.template
        variables = self.get_variables()
        template.add_version("2010-09-09")
        template.add_description("Kubernetes Master via EKS - V1.0.0")

        # Resources
        ccpsecuritygroup = template.add_resource(
            ec2.SecurityGroup(
                "ClusterControlPlaneSecurityGroup",
                GroupDescription="Cluster communication with worker nodes",
                Tags=[
                    {
                        "Key": Sub("kubernetes.io/cluster/${EksClusterName}"),
                        "Value": "owned",
                    },
                    {
                        "Key": "Product",
                        "Value": "Kubernetes"
                    },
                    {
                        "Key": "Project",
                        "Value": "eks"
                    },
                    {
                        "Key": "Name",
                        "Value": Sub("${EksClusterName}-sg-worker-nodes")
                    },
                ],
                VpcId=variables["VPC"].ref,
            ))
        template.add_output(
            Output(
                ccpsecuritygroup.title,
                Description="Cluster communication with worker nodes",
                Export=Export(
                    Sub("${AWS::StackName}-ControlPlaneSecurityGroup")),
                Value=ccpsecuritygroup.ref(),
            ))

        eksservicerole = template.add_resource(
            iam.Role(
                "EksServiceRole",
                AssumeRolePolicyDocument=make_simple_assume_policy(
                    "eks.amazonaws.com"),
                ManagedPolicyArns=[
                    IAM_POLICY_ARN_PREFIX + "AmazonEKSClusterPolicy"
                ],
                Policies=[
                    iam.Policy(
                        PolicyName="EksServiceRolePolicy",
                        PolicyDocument=PolicyDocument(Statement=[
                            Statement(
                                Action=[
                                    awacs.iam.CreateServiceLinkedRole,
                                    awacs.iam.PutRolePolicy,
                                ],
                                Condition=Condition(
                                    StringLike(
                                        "iam:AWSServiceName",
                                        "elasticloadbalancing.amazonaws.com",
                                    )),
                                Effect=Allow,
                                Resource=[
                                    Sub("arn:aws:iam::${AWS::AccountId}:role/"
                                        "aws-service-role/"
                                        "elasticloadbalancing.amazonaws.com/"
                                        "AWSServiceRoleForElasticLoadBalancing*"
                                        )
                                ],
                            )
                        ]),
                    )
                ],
            ))

        ekscluster = template.add_resource(
            eks.Cluster(
                "EksCluster",
                Name=variables["EksClusterName"].ref,
                Version=variables["EksVersion"].ref,
                RoleArn=eksservicerole.get_att("Arn"),
                ResourcesVpcConfig=eks.ResourcesVpcConfig(
                    SecurityGroupIds=[ccpsecuritygroup.ref()],
                    SubnetIds=variables["EksSubnets"].ref,
                ),
            ))
        template.add_output(
            Output(
                "%sName" % ekscluster.title,
                Description="EKS Cluster Name",
                Export=Export(
                    Sub("${AWS::StackName}-%sName" % ekscluster.title)),
                Value=ekscluster.ref(),
            ))
        template.add_output(
            Output(
                "%sEndpoint" % ekscluster.title,
                Description="EKS Cluster Endpoint",
                Export=Export(
                    Sub("${AWS::StackName}-%sEndpoint" % ekscluster.title)),
                Value=ekscluster.get_att("Endpoint"),
            ))

        # Additional Outputs
        template.add_output(
            Output(
                "VpcId",
                Description="EKS Cluster VPC Id",
                Export=Export(Sub("${AWS::StackName}-VpcId")),
                Value=variables["VPC"].ref,
            ))
        template.add_output(
            Output(
                "Subnets",
                Description="EKS Cluster Subnets",
                Export=Export(Sub("${AWS::StackName}-Subnets")),
                Value=Join(",", variables["EksSubnets"].ref),
            ))
Example #6
0
    def script_manager_ecr_deploy(self, ecr_deploy_group, asg_dict, asg_config, template):
        policy_statements = []
        for ecr_deploy_name in ecr_deploy_group.keys():
            ecr_deploy = ecr_deploy_group[ecr_deploy_name]
            if ecr_deploy == None:
                continue

            if ecr_deploy and len(ecr_deploy.release_phase.ecs) > 0:
                pull_repos = []
                push_repos = []
                for repository in ecr_deploy.repositories:
                    source_ecr_obj = get_model_obj_from_ref(repository.source_repo, self.paco_ctx.project)
                    source_env = get_parent_by_interface(source_ecr_obj, schemas.IEnvironmentRegion)
                    source_account_id = self.paco_ctx.get_ref(source_env.network.aws_account+".id")

                    dest_ecr_obj = get_model_obj_from_ref(repository.dest_repo, self.paco_ctx.project)
                    dest_env = get_parent_by_interface(dest_ecr_obj, schemas.IEnvironmentRegion)
                    dest_account_id = self.paco_ctx.get_ref(dest_env.network.aws_account+".id")

                    pull_repo_arn = f'arn:aws:ecr:{source_env.region}:{source_account_id}:repository/{source_ecr_obj.repository_name}'
                    push_repo_arn = f'arn:aws:ecr:{dest_env.region}:{dest_account_id}:repository/{dest_ecr_obj.repository_name}'
                    pull_repos.append(pull_repo_arn)
                    push_repos.append(push_repo_arn)

                policy_statements.append(
                    Statement(
                        Sid=f'ScriptManagerECRDeployPull',
                        Effect=Allow,
                        Action=[
                            Action('ecr', 'GetDownloadUrlForLayer'),
                            Action('ecr', 'BatchGetImage'),
                        ],
                        Resource=pull_repos
                    )
                )

                policy_statements.append(
                    Statement(
                        Sid=f'ScriptManagerECRDeployPush',
                        Effect=Allow,
                        Action=[
                            Action('ecr', 'GetDownloadUrlForLayer'),
                            Action('ecr', 'BatchCheckLayerAvailability'),
                            Action('ecr', 'PutImage'),
                            Action('ecr', 'InitiateLayerUpload'),
                            Action('ecr', 'UploadLayerPart'),
                            Action('ecr', 'CompleteLayerUpload'),
                        ],
                        Resource=push_repos
                    )
                )
                iam_cluster_cache = []
                idx = 0
                for command in ecr_deploy.release_phase.ecs:
                    service_obj = get_model_obj_from_ref(command.service, self.paco_ctx.project)
                    ecs_services_obj = get_parent_by_interface(service_obj, schemas.IECSServices)
                    ecs_release_phase_cluster_arn_param = self.create_cfn_parameter(
                        param_type='String',
                        name=f'ECSReleasePhaseClusterArn{idx}',
                        description=f'ECS Release Phase Cluster Arn {idx}',
                        value=ecs_services_obj.cluster + '.arn'
                    )
                    ecs_release_phase_cluster_name_param = self.create_cfn_parameter(
                        param_type='String',
                        name=f'ECSReleasePhaseClusterName{idx}',
                        description=f'ECS Release Phase Cluster Name {idx}',
                        value=ecs_services_obj.cluster + '.name'
                    )
                    ecs_release_phase_service_name_param = self.create_cfn_parameter(
                        param_type='String',
                        name=f'ECSReleasePhaseServiceName{idx}',
                        description=f'ECS Release Phase Cluster Name {idx}',
                        value=command.service + '.name'
                    )
                    ecs_cluster_asg_tag = troposphere.autoscaling.Tag(
                        f'PACO_CB_RP_ECS_CLUSTER_ID_{idx}',
                        troposphere.Ref(ecs_release_phase_cluster_name_param),
                        True
                    )
                    ecs_service_asg_tag = troposphere.autoscaling.Tag(
                        f'PACO_CB_RP_ECS_SERVICE_ID_{idx}',
                        troposphere.Ref(ecs_release_phase_service_name_param),
                        True
                    )
                    asg_dict['Tags'].append(ecs_cluster_asg_tag)
                    asg_dict['Tags'].append(ecs_service_asg_tag)


                    if ecs_services_obj.cluster not in iam_cluster_cache:
                        policy_statements.append(
                            Statement(
                                Sid=f'ECSReleasePhaseSSMSendCommand{idx}',
                                Effect=Allow,
                                Action=[
                                    Action('ssm', 'SendCommand'),
                                ],
                                Resource=[ 'arn:aws:ec2:*:*:instance/*' ],
                                Condition=Condition(
                                    StringLike({
                                        'ssm:resourceTag/Paco-ECSCluster-Name': troposphere.Ref(ecs_release_phase_cluster_name_param)
                                    })
                                )
                            )
                        )

                        policy_statements.append(
                            Statement(
                                Sid=f'ECSRelasePhaseClusterAccess{idx}',
                                Effect=Allow,
                                Action=[
                                    Action('ecs', 'DescribeServices'),
                                    Action('ecs', 'RunTask'),
                                    Action('ecs', 'StopTask'),
                                    Action('ecs', 'DescribeContainerInstances'),
                                    Action('ecs', 'ListTasks'),
                                    Action('ecs', 'DescribeTasks'),
                                ],
                                Resource=[ '*' ],
                                Condition=Condition(
                                    StringEquals({
                                        'ecs:cluster': troposphere.Ref(ecs_release_phase_cluster_arn_param)
                                    })
                                )
                            )
                        )
                        iam_cluster_cache.append(ecs_services_obj.cluster)

                    idx += 1

                policy_statements.append(
                    Statement(
                        Sid='ECSReleasePhaseSSMAutomationExecution',
                        Effect=Allow,
                            Action=[
                            Action('ssm', 'StartAutomationExecution'),
                            Action('ssm', 'StopAutomationExecution'),
                            Action('ssm', 'GetAutomationExecution'),
                        ],
                        Resource=[ 'arn:aws:ssm:::automation-definition/' ]
                    )
                )
                # ECS Policies
                policy_statements.append(
                    Statement(
                        Sid='ECSRelasePhaseECS',
                        Effect=Allow,
                        Action=[
                            Action('ecs', 'DescribeTaskDefinition'),
                            Action('ecs', 'DeregisterTaskDefinition'),
                            Action('ecs', 'RegisterTaskDefinition'),
                            Action('ecs', 'ListTagsForResource'),
                            Action('ecr', 'DescribeImages')
                        ],
                        Resource=[ '*' ]
                    )
                )

                policy_statements.append(
                    Statement(
                        Sid=f'ECSReleasePhaseSSMSendCommandDocument',
                        Effect=Allow,
                        Action=[
                            Action('ssm', 'SendCommand'),
                        ],
                        Resource=[ f'arn:aws:ssm:{self.aws_region}:{self.account_ctx.get_id()}:document/paco_ecs_docker_exec' ]
                    )
                )
                policy_statements.append(
                    Statement(
                        Sid='ECSReleasePhaseSSMCore',
                        Effect=Allow,
                        Action=[
                            Action('ssm', 'ListDocuments'),
                            Action('ssm', 'ListDocumentVersions'),
                            Action('ssm', 'DescribeDocument'),
                            Action('ssm', 'GetDocument'),
                            Action('ssm', 'DescribeInstanceInformation'),
                            Action('ssm', 'DescribeDocumentParameters'),
                            Action('ssm', 'CancelCommand'),
                            Action('ssm', 'ListCommands'),
                            Action('ssm', 'ListCommandInvocations'),
                            Action('ssm', 'DescribeAutomationExecutions'),
                            Action('ssm', 'DescribeInstanceProperties'),
                            Action('ssm', 'GetCommandInvocation'),
                            Action('ec2', 'DescribeInstanceStatus'),
                            Action('ecr', 'GetAuthorizationToken')
                        ],
                        Resource=[ '*' ]
                    )
                )

                policy_statements.append(
                    Statement(
                        Sid='IAMPassRole',
                        Effect=Allow,
                        Action=[
                            Action('iam', 'passrole')
                        ],
                        Resource=[ '*' ]
                    )
                )
                ecs_release_phase_project_policy_res = troposphere.iam.ManagedPolicy(
                    title='ECSReleasePhase',
                    PolicyDocument=PolicyDocument(
                        Version="2012-10-17",
                        Statement=policy_statements
                    ),
                    Roles=[self.instance_iam_role_name]
                )
                template.add_resource(ecs_release_phase_project_policy_res)
Example #7
0
    def create_template(self):
        """Create template (main function called by Stacker)."""
        template = self.template
        variables = self.get_variables()
        template.add_version('2010-09-09')
        template.add_description('Kubernetes Master via EKS - V1.0.0')

        # Resources
        ccpsecuritygroup = template.add_resource(
            ec2.SecurityGroup(
                'ClusterControlPlaneSecurityGroup',
                GroupDescription='Cluster communication with worker nodes',
                Tags=[
                    {'Key': Sub('kubernetes.io/cluster/${EksClusterName}'),
                     'Value': 'owned'},
                    {'Key': 'Product',
                     'Value': 'Kubernetes'},
                    {'Key': 'Project',
                     'Value': 'eks'},
                    {'Key': 'Name',
                     'Value': Sub('${EksClusterName}-sg-worker-nodes')}
                ],
                VpcId=variables['VPC'].ref
            )
        )
        template.add_output(
            Output(
                ccpsecuritygroup.title,
                Description='Cluster communication with worker nodes',
                Export=Export(
                    Sub('${AWS::StackName}-ControlPlaneSecurityGroup')
                ),
                Value=ccpsecuritygroup.ref()
            )
        )

        eksservicerole = template.add_resource(
            iam.Role(
                'EksServiceRole',
                AssumeRolePolicyDocument=make_simple_assume_policy(
                    'eks.amazonaws.com'
                ),
                ManagedPolicyArns=[
                    IAM_POLICY_ARN_PREFIX + i for i in [
                        'AmazonEKSClusterPolicy',
                        'AmazonEKSServicePolicy'
                    ]
                ],
                Policies=[
                    iam.Policy(
                        PolicyName='EksServiceRolePolicy',
                        PolicyDocument=PolicyDocument(
                            Statement=[
                                Statement(
                                    Action=[awacs.iam.CreateServiceLinkedRole,
                                            awacs.iam.PutRolePolicy],
                                    Condition=Condition(
                                        StringLike(
                                            'iam:AWSServiceName',
                                            'elasticloadbalancing.amazonaws.com'  # noqa
                                        )
                                    ),
                                    Effect=Allow,
                                    Resource=[
                                        Sub('arn:aws:iam::${AWS::AccountId}:role/'  # noqa
                                            'aws-service-role/'
                                            'elasticloadbalancing.amazonaws.com/'  # noqa
                                            'AWSServiceRoleForElasticLoadBalancing*')  # noqa
                                    ]
                                )
                            ]
                        )
                    )
                ]
            )
        )

        ekscluster = template.add_resource(
            eks.Cluster(
                'EksCluster',
                Name=variables['EksClusterName'].ref,
                Version=variables['EksVersion'].ref,
                RoleArn=eksservicerole.get_att('Arn'),
                ResourcesVpcConfig=eks.ResourcesVpcConfig(
                    SecurityGroupIds=[ccpsecuritygroup.ref()],
                    SubnetIds=variables['EksSubnets'].ref
                )
            )
        )
        template.add_output(
            Output(
                "%sName" % ekscluster.title,
                Description='EKS Cluster Name',
                Export=Export(
                    Sub("${AWS::StackName}-%sName" % ekscluster.title)
                ),
                Value=ekscluster.ref()
            )
        )
        template.add_output(
            Output(
                "%sEndpoint" % ekscluster.title,
                Description='EKS Cluster Endpoint',
                Export=Export(
                    Sub("${AWS::StackName}-%sEndpoint" % ekscluster.title)
                ),
                Value=ekscluster.get_att('Endpoint')
            )
        )

        # Additional Outputs
        template.add_output(
            Output(
                'VpcId',
                Description='EKS Cluster VPC Id',
                Export=Export(
                    Sub('${AWS::StackName}-VpcId')
                ),
                Value=variables['VPC'].ref
            )
        )
        template.add_output(
            Output(
                'Subnets',
                Description='EKS Cluster Subnets',
                Export=Export(
                    Sub('${AWS::StackName}-Subnets')
                ),
                Value=Join(',', variables['EksSubnets'].ref)
            )
        )
Example #8
0
    def create_codebuild_cfn(self, template, pipeline_config, action_config,
                             config_ref):
        # CodeBuild
        compute_type_param = self.create_cfn_parameter(
            param_type='String',
            name='CodeBuildComputeType',
            description=
            'The type of compute environment. This determines the number of CPU cores and memory the build environment uses.',
            value=action_config.codebuild_compute_type,
        )
        image_param = self.create_cfn_parameter(
            param_type='String',
            name='CodeBuildImage',
            description=
            'The image tag or image digest that identifies the Docker image to use for this build project.',
            value=action_config.codebuild_image,
        )
        deploy_env_name_param = self.create_cfn_parameter(
            param_type='String',
            name='DeploymentEnvironmentName',
            description=
            'The name of the environment codebuild will be deploying into.',
            value=action_config.deployment_environment,
        )
        # If ECS Release Phase, then create the needed parameters
        release_phase = action_config.release_phase
        ecs_release_phase_cluster_arn_param = []
        ecs_release_phase_cluster_name_param = []
        ecs_release_phase_service_arn_param = []
        if release_phase != None and release_phase.ecs != None:
            idx = 0
            for command in release_phase.ecs:
                service_obj = get_model_obj_from_ref(command.service,
                                                     self.paco_ctx.project)
                service_obj = get_parent_by_interface(service_obj,
                                                      schemas.IECSServices)
                cluster_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'ReleasePhaseECSClusterArn{idx}',
                    description='ECS Cluster Arn',
                    value=service_obj.cluster + '.arn',
                )
                ecs_release_phase_cluster_arn_param.append(cluster_arn_param)
                cluster_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'ReleasePhaseECSClusterName{idx}',
                    description='ECS Cluster Name',
                    value=service_obj.cluster + '.name',
                )
                ecs_release_phase_cluster_name_param.append(cluster_arn_param)
                service_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'ReleasePhaseECSServiceArn{idx}',
                    description='ECS Service Arn',
                    value=command.service + '.arn',
                )
                ecs_release_phase_service_arn_param.append(service_arn_param)
                idx += 1
        self.project_role_name = self.create_iam_resource_name(
            name_list=[self.res_name_prefix, 'CodeBuild-Project'],
            filter_id='IAM.Role.RoleName')

        # codecommit_repo_users ManagedPolicies
        managed_policy_arns = []
        for user_ref in action_config.codecommit_repo_users:
            user = get_model_obj_from_ref(user_ref, self.paco_ctx.project)
            # codecommit_stack = user.__parent__.__parent__.__parent__.stack
            user_logical_id = self.gen_cf_logical_name(user.username)
            codecommit_user_policy_param = self.create_cfn_parameter(
                param_type='String',
                name='CodeCommitUserPolicy' + user_logical_id,
                description='The CodeCommit User Policy for ' + user.username,
                value=user_ref + '.policy.arn',
            )
            managed_policy_arns.append(
                troposphere.Ref(codecommit_user_policy_param))

        project_role_res = troposphere.iam.Role(
            title='CodeBuildProjectRole',
            template=template,
            RoleName=self.project_role_name,
            ManagedPolicyArns=managed_policy_arns,
            AssumeRolePolicyDocument=PolicyDocument(
                Version="2012-10-17",
                Statement=[
                    Statement(
                        Effect=Allow,
                        Action=[AssumeRole],
                        Principal=Principal("Service",
                                            ['codebuild.amazonaws.com']),
                    )
                ]))

        project_policy_name = self.create_iam_resource_name(
            name_list=[self.res_name_prefix, 'CodeBuild-Project'],
            filter_id='IAM.Policy.PolicyName')

        # Project Policy
        policy_statements = []
        if self.enable_artifacts_bucket:
            policy_statements.append(
                Statement(
                    Sid='S3Access',
                    Effect=Allow,
                    Action=[
                        Action('s3', 'PutObject'),
                        Action('s3', 'PutObjectAcl'),
                        Action('s3', 'GetObject'),
                        Action('s3', 'GetObjectAcl'),
                        Action('s3', 'ListBucket'),
                        Action('s3', 'DeleteObject'),
                        Action('s3', 'GetBucketPolicy'),
                        Action('s3', 'HeadObject'),
                    ],
                    Resource=[
                        troposphere.Sub('arn:aws:s3:::${ArtifactsBucketName}'),
                        troposphere.Sub(
                            'arn:aws:s3:::${ArtifactsBucketName}/*'),
                    ]))
        if pipeline_config.configuration.disable_codepipeline == False:
            policy_statements.append(
                Statement(Sid='KMSCMK',
                          Effect=Allow,
                          Action=[Action('kms', '*')],
                          Resource=[troposphere.Ref(self.cmk_arn_param)]))
        policy_statements.append(
            Statement(Sid='CloudWatchLogsAccess',
                      Effect=Allow,
                      Action=[
                          Action('logs', 'CreateLogGroup'),
                          Action('logs', 'CreateLogStream'),
                          Action('logs', 'PutLogEvents'),
                      ],
                      Resource=['arn:aws:logs:*:*:*']))

        release_phase = action_config.release_phase
        if release_phase != None and release_phase.ecs != None:
            ssm_doc = self.paco_ctx.project['resource']['ssm'].ssm_documents[
                'paco_ecs_docker_exec']
            # SSM Exec Document
            policy_statements.append(
                Statement(Sid='ECSReleasePhaseSSMCore',
                          Effect=Allow,
                          Action=[
                              Action('ssm', 'ListDocuments'),
                              Action('ssm', 'ListDocumentVersions'),
                              Action('ssm', 'DescribeDocument'),
                              Action('ssm', 'GetDocument'),
                              Action('ssm', 'DescribeInstanceInformation'),
                              Action('ssm', 'DescribeDocumentParameters'),
                              Action('ssm', 'CancelCommand'),
                              Action('ssm', 'ListCommands'),
                              Action('ssm', 'ListCommandInvocations'),
                              Action('ssm', 'DescribeAutomationExecutions'),
                              Action('ssm', 'DescribeInstanceProperties'),
                              Action('ssm', 'GetCommandInvocation'),
                              Action('ec2', 'DescribeInstanceStatus'),
                          ],
                          Resource=['*']))
            policy_statements.append(
                Statement(
                    Sid=f'ECSReleasePhaseSSMSendCommandDocument',
                    Effect=Allow,
                    Action=[
                        Action('ssm', 'SendCommand'),
                    ],
                    Resource=[
                        f'arn:aws:ssm:{self.aws_region}:{self.account_ctx.get_id()}:document/paco_ecs_docker_exec'
                    ]))
            idx = 0
            for command in release_phase.ecs:
                policy_statements.append(
                    Statement(
                        Sid=f'ECSReleasePhaseSSMSendCommand{idx}',
                        Effect=Allow,
                        Action=[
                            Action('ssm', 'SendCommand'),
                        ],
                        Resource=[f'arn:aws:ec2:*:*:instance/*'],
                        Condition=Condition(
                            StringLike({
                                'ssm:resourceTag/Paco-ECSCluster-Name':
                                troposphere.Ref(
                                    ecs_release_phase_cluster_name_param[idx])
                            }))))

                policy_statements.append(
                    Statement(
                        Sid=f'ECSRelasePhaseClusterAccess{idx}',
                        Effect=Allow,
                        Action=[
                            Action('ecs', 'DescribeServices'),
                            Action('ecs', 'RunTask'),
                            Action('ecs', 'StopTask'),
                            Action('ecs', 'DescribeContainerInstances'),
                            Action('ecs', 'ListTasks'),
                            Action('ecs', 'DescribeTasks'),
                        ],
                        Resource=['*'],
                        Condition=Condition(
                            StringEquals({
                                'ecs:cluster':
                                troposphere.Ref(
                                    ecs_release_phase_cluster_arn_param[idx])
                            }))))
                idx += 1

            policy_statements.append(
                Statement(Sid='ECSReleasePhaseSSMAutomationExecution',
                          Effect=Allow,
                          Action=[
                              Action('ssm', 'StartAutomationExecution'),
                              Action('ssm', 'StopAutomationExecution'),
                              Action('ssm', 'GetAutomationExecution'),
                          ],
                          Resource=['arn:aws:ssm:::automation-definition/']))
            # ECS Policies
            policy_statements.append(
                Statement(Sid='ECSRelasePhaseECS',
                          Effect=Allow,
                          Action=[
                              Action('ecs', 'DescribeTaskDefinition'),
                              Action('ecs', 'DeregisterTaskDefinition'),
                              Action('ecs', 'RegisterTaskDefinition'),
                              Action('ecs', 'ListTagsForResource'),
                              Action('ecr', 'DescribeImages')
                          ],
                          Resource=['*']))

            # IAM Pass Role
            policy_statements.append(
                Statement(Sid='IAMPassRole',
                          Effect=Allow,
                          Action=[Action('iam', 'passrole')],
                          Resource=['*']))

        if len(action_config.secrets) > 0:
            secrets_arn_list = []
            for secret_ref in action_config.secrets:
                name_hash = md5sum(str_data=secret_ref)
                secret_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name='SecretsArn' + name_hash,
                    description=
                    'Secrets Manager Secret Arn to expose access to',
                    value=secret_ref + '.arn')
                secrets_arn_list.append(troposphere.Ref(secret_arn_param))
            policy_statements.append(
                Statement(Sid='SecretsManager',
                          Effect=Allow,
                          Action=[
                              Action('secretsmanager', 'GetSecretValue'),
                          ],
                          Resource=secrets_arn_list))

        project_policy_res = troposphere.iam.PolicyType(
            title='CodeBuildProjectPolicy',
            PolicyName=project_policy_name,
            PolicyDocument=PolicyDocument(Statement=policy_statements),
            Roles=[troposphere.Ref(project_role_res)])
        project_policy_res.DependsOn = project_role_res
        template.add_resource(project_policy_res)

        # User defined policies
        for policy in action_config.role_policies:
            policy_name = self.create_resource_name_join(
                name_list=[
                    self.res_name_prefix, 'CodeBuild-Project', policy.name
                ],
                separator='-',
                filter_id='IAM.Policy.PolicyName',
                hash_long_names=True,
                camel_case=True)
            statement_list = []

            for statement in policy.statement:
                action_list = []
                for action in statement.action:
                    action_parts = action.split(':')
                    action_list.append(Action(action_parts[0],
                                              action_parts[1]))
                statement_list.append(
                    Statement(Effect=statement.effect,
                              Action=action_list,
                              Resource=statement.resource))
            troposphere.iam.PolicyType(
                title=self.create_cfn_logical_id('CodeBuildProjectPolicy' +
                                                 policy.name,
                                                 camel_case=True),
                template=template,
                PolicyName=policy_name,
                PolicyDocument=PolicyDocument(Statement=statement_list, ),
                Roles=[troposphere.Ref(project_role_res)])

        # ECR Permission Policies
        self.set_ecr_repositories_statements(
            action_config.ecr_repositories, template,
            f'{self.res_name_prefix}-CodeBuild-Project',
            [troposphere.Ref(project_role_res)])

        # CodeBuild Project Resource
        timeout_mins_param = self.create_cfn_parameter(
            param_type='String',
            name='TimeoutInMinutes',
            description=
            'How long, in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait before timing out any related build that did not get marked as completed.',
            value=action_config.timeout_mins,
        )

        # Environment Variables
        codebuild_env_vars = [{
            'Name': 'DeploymentEnvironmentName',
            'Value': troposphere.Ref(deploy_env_name_param)
        }]
        if pipeline_config.configuration.disable_codepipeline == False:
            codebuild_env_vars.append({
                'Name':
                'KMSKey',
                'Value':
                troposphere.Ref(self.cmk_arn_param)
            })
        if self.enable_artifacts_bucket:
            codebuild_env_vars.append({
                'Name':
                'ArtifactsBucket',
                'Value':
                troposphere.Ref(self.artifacts_bucket_name_param),
            })
        # If ECS Release Phase, then add the config to the environment
        release_phase = action_config.release_phase
        if release_phase != None and release_phase.ecs != None:
            idx = 0
            for command in release_phase.ecs:
                codebuild_env_vars.append({
                    'Name':
                    f'PACO_CB_RP_ECS_CLUSTER_ID_{idx}',
                    'Value':
                    troposphere.Ref(ecs_release_phase_cluster_arn_param[idx])
                })
                codebuild_env_vars.append({
                    'Name':
                    f'PACO_CB_RP_ECS_SERVICE_ID_{idx}',
                    'Value':
                    troposphere.Ref(ecs_release_phase_service_arn_param[idx])
                })
                idx += 1

        # CodeBuild: Environment
        project_dict = {
            'Name':
            troposphere.Ref(self.resource_name_prefix_param),
            'Artifacts': {
                'Type': 'NO_ARTIFACTS'
            },
            'Description':
            troposphere.Ref('AWS::StackName'),
            'ServiceRole':
            troposphere.GetAtt('CodeBuildProjectRole', 'Arn'),
            'Environment': {
                'Type': 'LINUX_CONTAINER',
                'ComputeType': troposphere.Ref(compute_type_param),
                'Image': troposphere.Ref(image_param),
                'EnvironmentVariables': codebuild_env_vars,
                'PrivilegedMode': action_config.privileged_mode
            },
            'Source': {
                'Type': 'NO_SOURCE'
            },
            'TimeoutInMinutes':
            troposphere.Ref(timeout_mins_param),
            'Tags':
            troposphere.codebuild.Tags(
                Name=troposphere.Ref(self.resource_name_prefix_param))
        }

        if action_config.buildspec:
            project_dict['Source']['BuildSpec'] = action_config.buildspec

        if pipeline_config.configuration.disable_codepipeline == False:
            project_dict['EncryptionKey'] = troposphere.Ref(self.cmk_arn_param)
            project_dict['Artifacts'] = {'Type': 'CODEPIPELINE'}
            project_dict['Source']['Type'] = 'CODEPIPELINE'
        else:
            if action_config.artifacts == None or action_config.artifacts.type == 'NO_ARTIFACTS':
                project_dict['Artifacts'] = {
                    'Type': 'NO_ARTIFACTS',
                }
            else:
                project_dict['Artifacts'] = {
                    'Type': action_config.artifacts.type,
                    'Location':
                    troposphere.Ref(self.artifacts_bucket_name_param),
                    'NamespaceType': action_config.artifacts.namespace_type,
                    'Packaging': action_config.artifacts.packaging,
                    'Name': action_config.artifacts.name
                }
                if action_config.artifacts.path != None:
                    project_dict['Artifacts'][
                        'Path'] = action_config.artifacts.path
            if action_config.source.github != None:
                github_config = action_config.source.github
                project_dict['Source']['Type'] = 'GITHUB'
                location = f'https://github.com/{github_config.github_owner}/{github_config.github_repository}.git'
                project_dict['Source']['Location'] = location
                project_dict['Source'][
                    'ReportBuildStatus'] = github_config.report_build_status
                if github_config.deployment_branch_name != None:
                    project_dict[
                        'SourceVersion'] = github_config.deployment_branch_name
            else:
                raise PacoException(
                    "CodeBuild source must be configured when Codepipeline is disabled."
                )

        if action_config.concurrent_build_limit > 0:
            project_dict[
                'ConcurrentBuildLimit'] = action_config.concurrent_build_limit

        if action_config.vpc_config != None:
            vpc_config = action_config.vpc_config
            vpc_id_param = self.create_cfn_parameter(
                name='VPC',
                param_type='AWS::EC2::VPC::Id',
                description='The VPC Id',
                value='paco.ref netenv.{}.<environment>.<region>.network.vpc.id'
                .format(self.env_ctx.netenv.name),
            )

            security_group_list = []
            for sg_ref in vpc_config.security_groups:
                ref = Reference(sg_ref)
                sg_param_name = self.gen_cf_logical_name('SecurityGroupId' +
                                                         ref.parts[-2] +
                                                         ref.parts[-1])
                sg_param = self.create_cfn_parameter(
                    name=sg_param_name,
                    param_type='String',
                    description='Security Group Id',
                    value=sg_ref + '.id',
                )
                security_group_list.append(troposphere.Ref(sg_param))

            # security_group_list_param = self.create_cfn_ref_list_param(
            #     param_type='List<AWS::EC2::SecurityGroup::Id>',
            #     name='SecurityGroupList',
            #     description='List of security group ids to attach to CodeBuild.',
            #     value=vpc_config.security_groups,
            #     ref_attribute='id',
            # )
            subnet_id_list = []
            subnet_arn_list = []
            az_size = self.env_ctx.netenv[self.account_ctx.name][
                self.aws_region].network.availability_zones
            for segment_ref in vpc_config.segments:
                for az_idx in range(1, az_size + 1):
                    # Subnet Ids
                    segment_name = self.create_cfn_logical_id(
                        f"Segment{segment_ref.split('.')[-1]}AZ{az_idx}")
                    subnet_id_param = self.create_cfn_parameter(
                        name=segment_name,
                        param_type='AWS::EC2::Subnet::Id',
                        description=
                        f'VPC Subnet Id in AZ{az_idx} for CodeBuild VPC Config',
                        value=segment_ref + f'.az{az_idx}.subnet_id')
                    subnet_id_list.append(troposphere.Ref(subnet_id_param))
                    # Subnet Arns
                    subnet_arn_param = self.create_cfn_parameter(
                        name=segment_name + 'Arn',
                        param_type='String',
                        description=
                        f'VPC Subnet Id ARN in AZ{az_idx} for CodeBuild VPC Config',
                        value=segment_ref + f'.az{az_idx}.subnet_id.arn')
                    subnet_arn_list.append(troposphere.Ref(subnet_arn_param))

            if len(subnet_id_list) == 0:
                raise PacoException(
                    "CodeBuild VPC Config must have at least one segment defined."
                )

            # VPC Config Permissions
            policy_statements.append(
                Statement(Sid='VpcConfigPermissions',
                          Effect=Allow,
                          Action=[
                              Action('ec2', 'CreateNetworkInterface'),
                              Action('ec2', 'DescribeDhcpOptions'),
                              Action('ec2', 'DescribeNetworkInterfaces'),
                              Action('ec2', 'DeleteNetworkInterface'),
                              Action('ec2', 'DescribeSubnets'),
                              Action('ec2', 'DescribeSecurityGroups'),
                              Action('ec2', 'DescribeVpcs'),
                          ],
                          Resource=['*']))
            policy_statements.append(
                Statement(
                    Sid='VpcConfigNetworkInterface',
                    Effect=Allow,
                    Action=[
                        Action('ec2', 'CreateNetworkInterfacePermission'),
                    ],
                    Resource=[
                        f'arn:aws:ec2:{self.aws_region}:{self.account_ctx.id}:network-interface/*'
                    ],
                    Condition=Condition([
                        StringEquals({
                            "ec2:AuthorizedService":
                            "codebuild.amazonaws.com"
                        }),
                        ArnEquals({"ec2:Subnet": subnet_arn_list})
                    ])))

            project_dict['VpcConfig'] = {
                'VpcId': troposphere.Ref(vpc_id_param),
                'SecurityGroupIds': security_group_list,
                'Subnets': subnet_id_list
            }

        # Batch Build Config
        batch_service_role_res = None
        if action_config.build_batch_config != None and action_config.build_batch_config.is_enabled(
        ):
            batch_config = action_config.build_batch_config

            batch_service_role_name = self.create_iam_resource_name(
                name_list=[
                    self.res_name_prefix, 'CodeBuild-BuildBatch-ServiceRole'
                ],
                filter_id='IAM.Role.RoleName')
            batch_service_role_res = troposphere.iam.Role(
                title='CodeBuildBuildBatchConfigServiceRole',
                template=template,
                RoleName=batch_service_role_name,
                AssumeRolePolicyDocument=PolicyDocument(
                    Version="2012-10-17",
                    Statement=[
                        Statement(
                            Effect=Allow,
                            Action=[AssumeRole],
                            Principal=Principal("Service",
                                                ['codebuild.amazonaws.com']),
                        )
                    ]))

            project_dict['BuildBatchConfig'] = {
                'BatchReportMode': batch_config.batch_report_mode,
                'CombineArtifacts': batch_config.combine_artifacts,
                'TimeoutInMins': batch_config.timeout_in_mins,
                'ServiceRole': troposphere.GetAtt(batch_service_role_res,
                                                  'Arn'),
                'Restrictions': {
                    'ComputeTypesAllowed':
                    batch_config.restrictions.compute_types_allowed,
                    'MaximumBuildsAllowed':
                    batch_config.restrictions.maximum_builds_allowed
                }
            }

        project_res = troposphere.codebuild.Project.from_dict(
            'CodeBuildProject', project_dict)
        project_res.DependsOn = project_policy_res
        if action_config.build_batch_config != None and action_config.build_batch_config.is_enabled(
        ):
            project_res.DependsOn = batch_service_role_res

        self.template.add_resource(project_res)

        if batch_service_role_res != None:
            build_batch_policy_statements = []
            build_batch_policy_statements.append(
                Statement(Sid='BatchServiceRole',
                          Effect=Allow,
                          Action=[
                              Action('codebuild', 'StartBuild'),
                              Action('codebuild', 'StopBuild'),
                              Action('codebuild', 'RetryBuild')
                          ],
                          Resource=[troposphere.GetAtt(project_res, 'Arn')]))

            batch_policy_name = self.create_iam_resource_name(
                name_list=[self.res_name_prefix, 'CodeBuild-BatchPolicy'],
                filter_id='IAM.Policy.PolicyName')
            batch_policy_res = troposphere.iam.PolicyType(
                title='CodeBuildBuildBatchPolicy',
                template=template,
                PolicyName=batch_policy_name,
                PolicyDocument=PolicyDocument(
                    Statement=build_batch_policy_statements),
                Roles=[troposphere.Ref(batch_service_role_res)])

            batch_policy_res.DependsOn = project_res

        self.create_output(title='ProjectArn',
                           value=troposphere.GetAtt(project_res, 'Arn'),
                           description='CodeBuild Project Arn',
                           ref=config_ref + '.project.arn')

        return project_res
Example #9
0
    def cloudtrail_template(self, cloudtrail):
        "Template for CloudTrail"
        # Troposphere Template Initialization
        self.init_template(f'KMS Customer Managed Key (CMK) for CloudTrail')
        users = []
        for user in cloudtrail.kms_users:
            if is_ref(user):
                user_obj = get_model_obj_from_ref(user, self.paco_ctx.project)
                user = user_obj.username
            users.append(
                f"arn:aws:iam::{self.paco_ctx.project['accounts']['master'].account_id}:user/{user}"
            )
        accounts = [
            f"arn:aws:cloudtrail:*:{account.account_id}:trail/*"
            for account in cloudtrail.get_accounts()
        ]
        cloudtrail_policy = PolicyDocument(
            Version='2012-10-17',
            Statement=[
                Statement(
                    Sid="Allows admin of the key",
                    Effect=Allow,
                    Principal=Principal(
                        "AWS",
                        [f'arn:aws:iam::{self.stack.account_ctx.id}:root']),
                    Action=[
                        awacs.kms.CreateAlias, awacs.kms.CreateCustomKeyStore,
                        awacs.kms.CreateGrant, awacs.kms.CreateKey,
                        awacs.kms.DescribeCustomKeyStores,
                        awacs.kms.DescribeKey, awacs.kms.EnableKey,
                        awacs.kms.EnableKeyRotation, awacs.kms.ListAliases,
                        awacs.kms.ListGrants, awacs.kms.ListKeyPolicies,
                        awacs.kms.ListKeys, awacs.kms.ListResourceTags,
                        awacs.kms.ListRetirableGrants, awacs.kms.PutKeyPolicy,
                        awacs.kms.UpdateAlias, awacs.kms.UpdateCustomKeyStore,
                        awacs.kms.UpdateKeyDescription, awacs.kms.RevokeGrant,
                        awacs.kms.DisableKey, awacs.kms.DisableKeyRotation,
                        awacs.kms.GetKeyPolicy, awacs.kms.GetKeyRotationStatus,
                        awacs.kms.GetParametersForImport,
                        awacs.kms.DeleteAlias, awacs.kms.DeleteCustomKeyStore,
                        awacs.kms.DeleteImportedKeyMaterial,
                        awacs.kms.ScheduleKeyDeletion,
                        awacs.kms.CancelKeyDeletion, awacs.kms.TagResource
                    ],
                    Resource=['*'],
                ),
                Statement(
                    Sid="Allow CloudTrail access",
                    Effect=Allow,
                    Principal=Principal("Service",
                                        ['cloudtrail.amazonaws.com']),
                    Action=[awacs.kms.DescribeKey],
                    Resource=['*'],
                ),
                Statement(Sid="Allow CloudTrail log decrypt permissions",
                          Effect=Allow,
                          Action=[awacs.kms.Decrypt],
                          Principal=Principal("AWS", users),
                          Resource=['*'],
                          Condition=Condition([
                              Null({
                                  "kms:EncryptionContext:aws:cloudtrail:arn":
                                  False
                              })
                          ])),
                Statement(Sid="Allow CloudTrail to encrypt logs",
                          Effect=Allow,
                          Principal=Principal("Service",
                                              ["cloudtrail.amazonaws.com"]),
                          Action=[awacs.kms.GenerateDataKey],
                          Resource=['*'],
                          Condition=Condition([
                              StringLike({
                                  "kms:EncryptionContext:aws:cloudtrail:arn":
                                  accounts
                              })
                          ])),
            ])
        kms_dict = {
            'Description': 'CMK for CloudTrail',
            'EnableKeyRotation': True,
            'KeyPolicy': cloudtrail_policy,
        }
        kms_res = troposphere.kms.Key.from_dict('KMS', kms_dict)
        self.template.add_resource(kms_res)

        # Outputs
        self.create_output(
            title='CMKArn',
            description="The CMK Arn",
            value=troposphere.GetAtt(kms_res, 'Arn'),
            ref=self.resource.paco_ref_parts + ".kms.arn",
        )
        self.create_output(
            title='CMKId',
            description="The CMK Id",
            value=troposphere.Ref(kms_res),
            ref=self.resource.paco_ref_parts + ".kms.id",
        )
Example #10
0
    def create_codebuild_cfn(self, template, pipeline_config, action_config,
                             config_ref):
        # CodeBuild
        compute_type_param = self.create_cfn_parameter(
            param_type='String',
            name='CodeBuildComputeType',
            description=
            'The type of compute environment. This determines the number of CPU cores and memory the build environment uses.',
            value=action_config.codebuild_compute_type,
        )
        image_param = self.create_cfn_parameter(
            param_type='String',
            name='CodeBuildImage',
            description=
            'The image tag or image digest that identifies the Docker image to use for this build project.',
            value=action_config.codebuild_image,
        )
        deploy_env_name_param = self.create_cfn_parameter(
            param_type='String',
            name='DeploymentEnvironmentName',
            description=
            'The name of the environment codebuild will be deploying into.',
            value=action_config.deployment_environment,
        )
        # If ECS Release Phase, then create the needed parameters
        release_phase = action_config.release_phase
        ecs_release_phase_cluster_arn_param = []
        ecs_release_phase_cluster_name_param = []
        ecs_release_phase_service_arn_param = []
        if release_phase != None and release_phase.ecs != None:
            idx = 0
            for command in release_phase.ecs:
                service_obj = get_model_obj_from_ref(command.service,
                                                     self.paco_ctx.project)
                service_obj = get_parent_by_interface(service_obj,
                                                      schemas.IECSServices)
                cluster_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'ReleasePhaseECSClusterArn{idx}',
                    description='ECS Cluster Arn',
                    value=service_obj.cluster + '.arn',
                )
                ecs_release_phase_cluster_arn_param.append(cluster_arn_param)
                cluster_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'ReleasePhaseECSClusterName{idx}',
                    description='ECS Cluster Name',
                    value=service_obj.cluster + '.name',
                )
                ecs_release_phase_cluster_name_param.append(cluster_arn_param)
                service_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name=f'ReleasePhaseECSServiceArn{idx}',
                    description='ECS Service Arn',
                    value=command.service + '.arn',
                )
                ecs_release_phase_service_arn_param.append(service_arn_param)
                idx += 1
        self.project_role_name = self.create_iam_resource_name(
            name_list=[self.res_name_prefix, 'CodeBuild-Project'],
            filter_id='IAM.Role.RoleName')

        # codecommit_repo_users ManagedPolicies
        managed_policy_arns = []
        for user_ref in action_config.codecommit_repo_users:
            user = get_model_obj_from_ref(user_ref, self.paco_ctx.project)
            # codecommit_stack = user.__parent__.__parent__.__parent__.stack
            user_logical_id = self.gen_cf_logical_name(user.username)
            codecommit_user_policy_param = self.create_cfn_parameter(
                param_type='String',
                name='CodeCommitUserPolicy' + user_logical_id,
                description='The CodeCommit User Policy for ' + user.username,
                value=user_ref + '.policy.arn',
            )
            managed_policy_arns.append(
                troposphere.Ref(codecommit_user_policy_param))

        project_role_res = troposphere.iam.Role(
            title='CodeBuildProjectRole',
            template=template,
            RoleName=self.project_role_name,
            ManagedPolicyArns=managed_policy_arns,
            AssumeRolePolicyDocument=PolicyDocument(
                Version="2012-10-17",
                Statement=[
                    Statement(
                        Effect=Allow,
                        Action=[AssumeRole],
                        Principal=Principal("Service",
                                            ['codebuild.amazonaws.com']),
                    )
                ]))

        project_policy_name = self.create_iam_resource_name(
            name_list=[self.res_name_prefix, 'CodeBuild-Project'],
            filter_id='IAM.Policy.PolicyName')

        # Project Policy
        policy_statements = []
        if pipeline_config.configuration.disable_codepipeline == False:
            policy_statements.extend([
                Statement(
                    Sid='S3Access',
                    Effect=Allow,
                    Action=[
                        Action('s3', 'PutObject'),
                        Action('s3', 'PutObjectAcl'),
                        Action('s3', 'GetObject'),
                        Action('s3', 'GetObjectAcl'),
                        Action('s3', 'ListBucket'),
                        Action('s3', 'DeleteObject'),
                        Action('s3', 'GetBucketPolicy'),
                        Action('s3', 'HeadObject'),
                    ],
                    Resource=[
                        troposphere.Sub('arn:aws:s3:::${ArtifactsBucketName}'),
                        troposphere.Sub(
                            'arn:aws:s3:::${ArtifactsBucketName}/*'),
                    ]),
                Statement(Sid='KMSCMK',
                          Effect=Allow,
                          Action=[Action('kms', '*')],
                          Resource=[troposphere.Ref(self.cmk_arn_param)])
            ])
        policy_statements.append(
            Statement(Sid='CloudWatchLogsAccess',
                      Effect=Allow,
                      Action=[
                          Action('logs', 'CreateLogGroup'),
                          Action('logs', 'CreateLogStream'),
                          Action('logs', 'PutLogEvents'),
                      ],
                      Resource=['arn:aws:logs:*:*:*']))

        release_phase = action_config.release_phase
        if release_phase != None and release_phase.ecs != None:
            ssm_doc = self.paco_ctx.project['resource']['ssm'].ssm_documents[
                'paco_ecs_docker_exec']
            # SSM Exec Document
            policy_statements.append(
                Statement(Sid='ECSReleasePhaseSSMCore',
                          Effect=Allow,
                          Action=[
                              Action('ssm', 'ListDocuments'),
                              Action('ssm', 'ListDocumentVersions'),
                              Action('ssm', 'DescribeDocument'),
                              Action('ssm', 'GetDocument'),
                              Action('ssm', 'DescribeInstanceInformation'),
                              Action('ssm', 'DescribeDocumentParameters'),
                              Action('ssm', 'CancelCommand'),
                              Action('ssm', 'ListCommands'),
                              Action('ssm', 'ListCommandInvocations'),
                              Action('ssm', 'DescribeAutomationExecutions'),
                              Action('ssm', 'DescribeInstanceProperties'),
                              Action('ssm', 'GetCommandInvocation'),
                              Action('ec2', 'DescribeInstanceStatus'),
                          ],
                          Resource=['*']))
            policy_statements.append(
                Statement(
                    Sid=f'ECSReleasePhaseSSMSendCommandDocument',
                    Effect=Allow,
                    Action=[
                        Action('ssm', 'SendCommand'),
                    ],
                    Resource=[
                        f'arn:aws:ssm:{self.aws_region}:{self.account_ctx.get_id()}:document/paco_ecs_docker_exec'
                    ]))
            idx = 0
            for command in release_phase.ecs:
                policy_statements.append(
                    Statement(
                        Sid=f'ECSReleasePhaseSSMSendCommand{idx}',
                        Effect=Allow,
                        Action=[
                            Action('ssm', 'SendCommand'),
                        ],
                        Resource=[f'arn:aws:ec2:*:*:instance/*'],
                        Condition=Condition(
                            StringLike({
                                'ssm:resourceTag/Paco-ECSCluster-Name':
                                troposphere.Ref(
                                    ecs_release_phase_cluster_name_param[idx])
                            }))))

                policy_statements.append(
                    Statement(
                        Sid=f'ECSRelasePhaseClusterAccess{idx}',
                        Effect=Allow,
                        Action=[
                            Action('ecs', 'DescribeServices'),
                            Action('ecs', 'RunTask'),
                            Action('ecs', 'StopTask'),
                            Action('ecs', 'DescribeContainerInstances'),
                            Action('ecs', 'ListTasks'),
                            Action('ecs', 'DescribeTasks'),
                        ],
                        Resource=['*'],
                        Condition=Condition(
                            StringEquals({
                                'ecs:cluster':
                                troposphere.Ref(
                                    ecs_release_phase_cluster_arn_param[idx])
                            }))))
                idx += 1

            policy_statements.append(
                Statement(Sid='ECSReleasePhaseSSMAutomationExecution',
                          Effect=Allow,
                          Action=[
                              Action('ssm', 'StartAutomationExecution'),
                              Action('ssm', 'StopAutomationExecution'),
                              Action('ssm', 'GetAutomationExecution'),
                          ],
                          Resource=['arn:aws:ssm:::automation-definition/']))
            # ECS Policies
            policy_statements.append(
                Statement(Sid='ECSRelasePhaseECS',
                          Effect=Allow,
                          Action=[
                              Action('ecs', 'DescribeTaskDefinition'),
                              Action('ecs', 'DeregisterTaskDefinition'),
                              Action('ecs', 'RegisterTaskDefinition'),
                              Action('ecs', 'ListTagsForResource'),
                              Action('ecr', 'DescribeImages')
                          ],
                          Resource=['*']))

            # IAM Pass Role
            policy_statements.append(
                Statement(Sid='IAMPassRole',
                          Effect=Allow,
                          Action=[Action('iam', 'passrole')],
                          Resource=['*']))

        if len(action_config.secrets) > 0:
            secrets_arn_list = []
            for secret_ref in action_config.secrets:
                name_hash = md5sum(str_data=secret_ref)
                secret_arn_param = self.create_cfn_parameter(
                    param_type='String',
                    name='SecretsArn' + name_hash,
                    description=
                    'Secrets Manager Secret Arn to expose access to',
                    value=secret_ref + '.arn')
                secrets_arn_list.append(troposphere.Ref(secret_arn_param))
            policy_statements.append(
                Statement(Sid='SecretsManager',
                          Effect=Allow,
                          Action=[
                              Action('secretsmanager', 'GetSecretValue'),
                          ],
                          Resource=secrets_arn_list))

        project_policy_res = troposphere.iam.PolicyType(
            title='CodeBuildProjectPolicy',
            PolicyName=project_policy_name,
            PolicyDocument=PolicyDocument(Statement=policy_statements),
            Roles=[troposphere.Ref(project_role_res)])
        template.add_resource(project_policy_res)

        # User defined policies
        for policy in action_config.role_policies:
            policy_name = self.create_resource_name_join(
                name_list=[
                    self.res_name_prefix, 'CodeBuild-Project', policy.name
                ],
                separator='-',
                filter_id='IAM.Policy.PolicyName',
                hash_long_names=True,
                camel_case=True)
            statement_list = []

            for statement in policy.statement:
                action_list = []
                for action in statement.action:
                    action_parts = action.split(':')
                    action_list.append(Action(action_parts[0],
                                              action_parts[1]))
                statement_list.append(
                    Statement(Effect=statement.effect,
                              Action=action_list,
                              Resource=statement.resource))
            troposphere.iam.PolicyType(
                title=self.create_cfn_logical_id('CodeBuildProjectPolicy' +
                                                 policy.name,
                                                 camel_case=True),
                template=template,
                PolicyName=policy_name,
                PolicyDocument=PolicyDocument(Statement=statement_list, ),
                Roles=[troposphere.Ref(project_role_res)])

        # ECR Permission Policies
        self.set_ecr_repositories_statements(
            action_config.ecr_repositories, template,
            f'{self.res_name_prefix}-CodeBuild-Project',
            [troposphere.Ref(project_role_res)])

        # CodeBuild Project Resource
        timeout_mins_param = self.create_cfn_parameter(
            param_type='String',
            name='TimeoutInMinutes',
            description=
            'How long, in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait before timing out any related build that did not get marked as completed.',
            value=action_config.timeout_mins,
        )

        # Environment Variables
        codebuild_env_vars = [{
            'Name': 'DeploymentEnvironmentName',
            'Value': troposphere.Ref(deploy_env_name_param)
        }]
        if pipeline_config.configuration.disable_codepipeline == False:
            codebuild_env_vars.extend([{
                'Name':
                'ArtifactsBucket',
                'Value':
                troposphere.Ref(self.artifacts_bucket_name_param),
            }, {
                'Name':
                'KMSKey',
                'Value':
                troposphere.Ref(self.cmk_arn_param)
            }])
        # If ECS Release Phase, then add the config to the environment
        release_phase = action_config.release_phase
        if release_phase != None and release_phase.ecs != None:
            idx = 0
            for command in release_phase.ecs:
                codebuild_env_vars.append({
                    'Name':
                    f'PACO_CB_RP_ECS_CLUSTER_ID_{idx}',
                    'Value':
                    troposphere.Ref(ecs_release_phase_cluster_arn_param[idx])
                })
                codebuild_env_vars.append({
                    'Name':
                    f'PACO_CB_RP_ECS_SERVICE_ID_{idx}',
                    'Value':
                    troposphere.Ref(ecs_release_phase_service_arn_param[idx])
                })
                idx += 1

        # CodeBuild: Environment
        source = troposphere.codebuild.Source(Type='CODEPIPELINE', )
        if action_config.buildspec != None and action_config.buildspec != '':
            source = troposphere.codebuild.Source(
                Type='CODEPIPELINE',
                BuildSpec=action_config.buildspec,
            )

        project_dict = {
            'Name':
            troposphere.Ref(self.resource_name_prefix_param),
            'Artifacts': {
                'Type': 'NO_ARTIFACTS'
            },
            'Description':
            troposphere.Ref('AWS::StackName'),
            'ServiceRole':
            troposphere.GetAtt('CodeBuildProjectRole', 'Arn'),
            'Environment': {
                'Type': 'LINUX_CONTAINER',
                'ComputeType': troposphere.Ref(compute_type_param),
                'Image': troposphere.Ref(image_param),
                'EnvironmentVariables': codebuild_env_vars,
                'PrivilegedMode': action_config.privileged_mode
            },
            'Source': {
                'Type': 'NO_SOURCE'
            },
            'TimeoutInMinutes':
            troposphere.Ref(timeout_mins_param),
            'Tags':
            troposphere.codebuild.Tags(
                Name=troposphere.Ref(self.resource_name_prefix_param))
        }

        if action_config.buildspec:
            project_dict['Source']['BuildSpec'] = action_config.buildspec

        if pipeline_config.configuration.disable_codepipeline == False:
            project_dict['EncryptionKey'] = troposphere.Ref(self.cmk_arn_param)
            project_dict['Artifacts'] = {'Type': 'CODEPIPELINE'}
            project_dict['Source']['Type'] = 'CODEPIPELINE'
        elif action_config.source.github != None:
            project_dict['Source']['Type'] = 'GITHUB'
            project_dict['Source'][
                'Location'] = action_config.source.github.location
            project_dict['Source'][
                'ReportBuildStatus'] = action_config.source.github.report_build_status
        else:
            raise PacoException(
                "CodeBuild source must be configured when Codepipeline is disabled."
            )

        if action_config.concurrent_build_limit > 0:
            project_dict[
                'ConcurrentBuildLimit'] = action_config.concurrent_build_limit

        project_res = troposphere.codebuild.Project.from_dict(
            'CodeBuildProject', project_dict)
        self.template.add_resource(project_res)

        self.create_output(title='ProjectArn',
                           value=troposphere.GetAtt(project_res, 'Arn'),
                           description='CodeBuild Project Arn',
                           ref=config_ref + '.project.arn')

        return project_res
Example #11
0
    def add_resources(self):
        """Add resources to template."""
        template = self.template
        variables = self.get_variables()

        vpnrole = template.add_resource(
            iam.Role(
                'VPNRole',
                AssumeRolePolicyDocument=iam_policies.assumerolepolicy('ec2'),
                ManagedPolicyArns=variables['VPNManagedPolicies'].ref,
                Path='/',
                Policies=[
                    iam.Policy(
                        PolicyName=Join('-', [
                            'customer-vpn-server-role',
                            variables['EnvironmentName'].ref,
                            variables['CustomerName'].ref
                        ]),
                        PolicyDocument=Policy(
                            Version='2012-10-17',
                            Statement=[
                                # ModifyInstanceAttribute is for src/dst check
                                Statement(Action=[
                                    awacs.ec2.DescribeRouteTables,
                                    awacs.ec2.DescribeAddresses,
                                    awacs.ec2.AssociateAddress,
                                    awacs.ec2.CreateRoute,
                                    awacs.ec2.ReplaceRoute,
                                    awacs.ec2.ModifyInstanceAttribute
                                ],
                                          Effect=Allow,
                                          Resource=['*']),
                                Statement(
                                    Action=[
                                        awacs.aws.Action('s3', 'Get*'),
                                        awacs.aws.Action('s3', 'List*'),
                                        awacs.aws.Action('s3', 'Put*')
                                    ],
                                    Effect=Allow,
                                    Resource=[
                                        Join(
                                            '',
                                            [
                                                'arn:aws:s3:::',
                                                variables['ChefDataBucketName']
                                                .ref,  # noqa pylint: disable=line-too-long
                                                '/',
                                                variables['EnvironmentName'].
                                                ref,
                                                '/',
                                                variables['BucketKey'].ref,
                                                '/*'
                                            ])
                                    ]),
                                Statement(
                                    Action=[awacs.s3.ListBucket],
                                    Effect=Allow,
                                    Resource=[
                                        Join('', [
                                            'arn:aws:s3:::',
                                            variables['ChefDataBucketName'].ref
                                        ])  # noqa pylint: disable=line-too-long
                                    ],
                                    Condition=Condition(
                                        StringLike(
                                            's3:prefix',
                                            [
                                                Join(
                                                    '',
                                                    [
                                                        variables[
                                                            'EnvironmentName'].
                                                        ref,  # noqa pylint: disable=line-too-long
                                                        '/',
                                                        variables['BucketKey'].
                                                        ref,  # noqa pylint: disable=line-too-long
                                                        '/*'
                                                    ])
                                            ])))
                            ]))
                ]))
        vpninstanceprofile = template.add_resource(
            iam.InstanceProfile('VPNInstanceProfile',
                                Path='/',
                                Roles=[Ref(vpnrole)]))

        amiid = template.add_resource(
            cfn_custom_classes.AMIId(
                'AMIId',
                Condition='MissingVPNAMI',
                Platform=variables['VPNOS'].ref,
                Region=Ref('AWS::Region'),
                ServiceToken=variables['AMILookupArn'].ref))

        # Lookup subnets from core VPC stack
        subnetlookuplambdarole = template.add_resource(
            iam.Role(
                'SubnetLookupLambdaRole',
                Condition='PrivateSubnetCountOmitted',
                AssumeRolePolicyDocument=iam_policies.assumerolepolicy(
                    'lambda'),
                ManagedPolicyArns=[
                    IAM_ARN_PREFIX + 'AWSLambdaBasicExecutionRole'
                ],
                Policies=[
                    iam.Policy(
                        PolicyName=Join('-', [
                            'subnetlookup-lambda-role',
                            variables['EnvironmentName'].ref,
                            variables['CustomerName'].ref
                        ]),
                        PolicyDocument=Policy(
                            Version='2012-10-17',
                            Statement=[
                                Statement(
                                    Action=[
                                        awacs.aws.Action(
                                            'cloudformation',
                                            'DescribeStack*'),
                                        awacs.aws.Action(
                                            'cloudformation', 'Get*')
                                    ],
                                    Effect=Allow,
                                    Resource=[
                                        Join('', [
                                            'arn:aws:cloudformation:',
                                            Ref('AWS::Region'), ':',
                                            Ref('AWS::AccountId'), ':stack/',
                                            variables['CoreVPCStack'].ref, '/*'
                                        ])
                                    ])
                            ]))
                ]))

        cfncustomresourcesubnetlookup = template.add_resource(
            awslambda.Function(
                'CFNCustomResourceSubnetLookup',
                Condition='PrivateSubnetCountOmitted',
                Description='Find subnets created by core stack',
                Code=awslambda.Code(
                    ZipFile=variables['SubnetLookupLambdaFunction']),
                Handler='index.handler',
                Role=GetAtt(subnetlookuplambdarole, 'Arn'),
                Runtime='python2.7',
                Timeout=10))

        subnetlookup = template.add_resource(
            cfn_custom_classes.SubnetLookup(
                'SubnetLookup',
                Condition='PrivateSubnetCountOmitted',
                CoreVPCStack=variables['CoreVPCStack'].ref,
                Region=Ref('AWS::Region'),
                ServiceToken=GetAtt(cfncustomresourcesubnetlookup, 'Arn')))

        common_userdata_prefix = [
            "#cloud-config\n",
            "package_update: true\n",
            "package_upgrade: false\n",
            "write_files:\n",
            "  - path: /usr/local/bin/update_vpn_routes.sh\n",
            "    permissions: '0755'\n",
            "    content: |\n",
            "      #!/bin/bash\n",
            "      \n",
            "      export AWS_DEFAULT_REGION=\"",
            Ref('AWS::Region'),
            "\"\n",
            "      my_instance_id=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)\n",  # noqa pylint: disable=line-too-long
            "      \n",
            "      publicroutetableid=",
            If(
                'PrivateSubnetCountOmitted',
                GetAtt(subnetlookup.title, 'PublicRouteTableId'),
                If(
                    'PublicRouteTableSpecified',
                    variables['PublicRouteTable'].ref,
                    ImportValue(
                        Sub("${%s}-PublicRouteTable" %
                            variables['CoreVPCStack'].name)))),  # noqa pylint: disable=line-too-long
            "\n",
            "      private_route_tables=(",
            If(
                'PrivateSubnetCountOmitted',
                GetAtt(subnetlookup.title, 'PrivateRouteTables'),
                If(
                    '3PrivateSubnetsCreated',
                    If(
                        'PublicRouteTableSpecified',
                        Join(' ', [
                            variables['PrivateRouteTable1'].ref,
                            variables['PrivateRouteTable2'].ref,
                            variables['PrivateRouteTable3'].ref
                        ]),
                        Join(
                            ' ',
                            [
                                ImportValue(
                                    Sub("${%s}-PrivateRouteTable1" %
                                        variables['CoreVPCStack'].name)),  # noqa pylint: disable=line-too-long
                                ImportValue(
                                    Sub("${%s}-PrivateRouteTable2" %
                                        variables['CoreVPCStack'].name)),  # noqa pylint: disable=line-too-long
                                ImportValue(
                                    Sub("${%s}-PrivateRouteTable3" %
                                        variables['CoreVPCStack'].name))
                            ])),  # noqa pylint: disable=line-too-long
                    If(
                        '2PrivateSubnetsCreated',
                        If(
                            'PublicRouteTableSpecified',
                            Join(' ', [
                                variables['PrivateRouteTable1'].ref,
                                variables['PrivateRouteTable2'].ref
                            ]),
                            Join(
                                ' ',
                                [
                                    ImportValue(
                                        Sub("${%s}-PrivateRouteTable1" %
                                            variables['CoreVPCStack'].name)),  # noqa pylint: disable=line-too-long
                                    ImportValue(
                                        Sub("${%s}-PrivateRouteTable2" %
                                            variables['CoreVPCStack'].name))
                                ])),  # noqa pylint: disable=line-too-long,
                        If(
                            'PublicRouteTableSpecified',
                            variables['PrivateRouteTable1'].ref,
                            ImportValue(
                                Sub("${%s}-PrivateRouteTable1" %
                                    variables['CoreVPCStack'].name)))))),  # noqa pylint: disable=line-too-long
            ")\n",
            "\n",
            "      openvpnroutepubdest=",
            variables['VPNSubnet'].ref,
            "\n",
            "      \n",
            "      # Disabling sourceDestCheck\n",
            "      aws ec2 modify-instance-attribute --instance-id ${my_instance_id} --source-dest-check \"{\\\"Value\\\": false}\"\n",  # noqa pylint: disable=line-too-long
            "      \n",
            "      if aws ec2 describe-route-tables | grep ${openvpnroutepubdest}; then\n",  # noqa pylint: disable=line-too-long
            "          # Update 'OpenVPNRoutePub' to point to this instance\n",  # noqa pylint: disable=line-too-long
            "          aws ec2 replace-route --route-table-id ${publicroutetableid} --destination-cidr-block ${openvpnroutepubdest} --instance-id ${my_instance_id}\n",  # noqa pylint: disable=line-too-long
            "          # Update private routes\n",
            "          for i in \"${private_route_tables[@]}\"\n",
            "          do\n",
            "              aws ec2 replace-route --route-table-id $i --destination-cidr-block ${openvpnroutepubdest} --instance-id ${my_instance_id}\n",  # noqa pylint: disable=line-too-long
            "          done\n",
            "      else\n",
            "          # Create 'OpenVPNRoutePub'\n",
            "          aws ec2 create-route --route-table-id ${publicroutetableid} --destination-cidr-block ${openvpnroutepubdest} --instance-id ${my_instance_id}\n",  # noqa pylint: disable=line-too-long
            "          # Create private routes\n",
            "          for i in \"${private_route_tables[@]}\"\n",
            "          do\n",
            "              aws ec2 create-route --route-table-id $i --destination-cidr-block ${openvpnroutepubdest} --instance-id ${my_instance_id}\n",  # noqa pylint: disable=line-too-long
            "          done\n",
            "      fi\n",
            "      \n",
            "\n",
            "  - path: /etc/chef/sync_cookbooks.sh\n",
            "    permissions: '0755'\n",
            "    owner: 'root'\n",
            "    group: 'root'\n",
            "    content: |\n",
            "      #!/bin/bash\n",
            "      set -e -o pipefail\n",
            "      \n",
            "      aws --region ",
            Ref('AWS::Region'),
            " s3 sync s3://",
            variables['ChefBucketName'].ref,
            "/",
            variables['EnvironmentName'].ref,
            "/",
            variables['BucketKey'].ref,
            "/ /etc/chef/\n",
            "      if compgen -G \"/etc/chef/cookbooks-*.tar.gz\" > /dev/null; then\n",  # noqa pylint: disable=line-too-long
            "          echo \"Cookbook archive found.\"\n",
            "          if [ -d \"/etc/chef/cookbooks\" ]; then\n",
            "              echo \"Removing previously extracted cookbooks.\"\n",  # noqa pylint: disable=line-too-long
            "              rm -r /etc/chef/cookbooks\n",
            "          fi\n",
            "          echo \"Extracting highest numbered cookbook archive.\"\n",  # noqa pylint: disable=line-too-long
            "          cbarchives=(/etc/chef/cookbooks-*.tar.gz)\n",
            "          tar -zxf \"${cbarchives[@]: -1}\" -C /etc/chef\n",
            "          chown -R root:root /etc/chef\n",
            "      fi\n",
            "      \n",
            "\n",
            "  - path: /etc/chef/perform_chef_run.sh\n",
            "    permissions: '0755'\n",
            "    owner: 'root'\n",
            "    group: 'root'\n",
            "    content: |\n",
            "      #!/bin/bash\n",
            "      set -e -o pipefail\n",
            "      \n",
            "      chef-client -z -r '",
            If('ChefRunListSpecified', variables['ChefRunList'].ref,
               Join('', ['recipe[', variables['CustomerName'].ref, '_vpn]'])),
            "' -c /etc/chef/client.rb -E ",
            variables['EnvironmentName'].ref,
            " --force-formatter --no-color -F min\n",
            "\n",
            "  - path: /etc/chef/client.rb\n",
            "    permissions: '0644'\n",
            "    owner: 'root'\n",
            "    group: 'root'\n",
            "    content: |\n",
            "      log_level :info\n",
            "      log_location '/var/log/chef/client.log'\n",
            "      ssl_verify_mode :verify_none\n",
            "      cookbook_path '/etc/chef/cookbooks'\n",
            "      node_path '/etc/chef/nodes'\n",
            "      role_path '/etc/chef/roles'\n",
            "      data_bag_path '/etc/chef/data_bags'\n",
            "      environment_path '/etc/chef/environments'\n",
            "      local_mode 'true'\n",
            "\n",
            "  - path: /etc/chef/environments/",
            variables['EnvironmentName'].ref,
            ".json\n",
            "    permissions: '0644'\n",
            "    owner: 'root'\n",
            "    group: 'root'\n",
            "    content: |\n",
            "      {\n",
            "        \"name\": \"",
            variables['EnvironmentName'].ref,
            "\",\n",
            "        \"default_attributes\": {\n",
            "          \"sturdy\": {\n",
            "            \"openvpn\": {\n",
            "              \"core_vpc_cidr\": \"",
            variables['VpcCidr'].ref,
            "\",\n",
            "              \"vpn_elastic_ip\": \"",
            variables['VpnEipPublicIp'].ref,
            "\",\n",
            "              \"vpn_subnet_cidr\": \"",
            variables['VPNSubnet'].ref,
            "\",\n",
            "              \"chef_data_bucket_name\": \"",
            variables['ChefDataBucketName'].ref,
            "\",\n",
            "              \"chef_data_bucket_folder\": \"",
            variables['EnvironmentName'].ref,
            "/",
            variables['BucketKey'].ref,
            "\",\n",
            "              \"chef_data_bucket_region\": \"",
            Ref('AWS::Region'),
            "\"\n",
            "            }\n",
            "          }\n",
            "        },\n",
            "        \"json_class\": \"Chef::Environment\",\n",
            "        \"description\": \"",
            variables['EnvironmentName'].ref,
            " environment\",\n",
            "        \"chef_type\": \"environment\"\n",
            "      }\n",
            "\n",
            "runcmd:\n",
            "  - set -euf\n",
            "  - echo 'Attaching EIP'\n",
            "  - pip install aws-ec2-assign-elastic-ip\n",
            # Allowing this command to fail (with ||true) as sturdy_openvpn
            # 2.3.0+ can handle this association instead. This will be removed
            # entirely in the next major release of this module (at which time
            # use of the updated sturdy_openvpn cookbook will be required)
            "  - aws-ec2-assign-elastic-ip --region ",
            Ref('AWS::Region'),
            " --valid-ips ",
            variables['VpnEipPublicIp'].ref,
            " || true\n",
            "  - echo 'Updating Routes'\n",
            "  - /usr/local/bin/update_vpn_routes.sh\n",
            "  - echo 'Installing Chef'\n",
            "  - curl --max-time 10 --retry-delay 5 --retry 5 -L https://www.chef.io/chef/install.sh | bash -s -- -v ",  # noqa pylint: disable=line-too-long
            variables['ChefClientVersion'].ref,
            "\n",
            "  - echo 'Configuring Chef'\n",
            "  - mkdir -p /var/log/chef /etc/chef/data_bags /etc/chef/nodes /etc/chef/roles\n",  # noqa pylint: disable=line-too-long
            "  - chmod 0755 /etc/chef\n",
            "  - /etc/chef/sync_cookbooks.sh\n",
            "  - /etc/chef/perform_chef_run.sh\n"
        ]

        vpnserverlaunchconfig = template.add_resource(
            autoscaling.LaunchConfiguration(
                'VpnServerLaunchConfig',
                AssociatePublicIpAddress=True,
                BlockDeviceMappings=[
                    # CentOS AMIs don't include this by default
                    ec2.BlockDeviceMapping(
                        DeviceName='/dev/sda1',
                        Ebs=ec2.EBSBlockDevice(DeleteOnTermination=True))
                ],
                IamInstanceProfile=Ref(vpninstanceprofile),
                ImageId=If('MissingVPNAMI', GetAtt(amiid, 'ImageId'),
                           variables['VPNAMI'].ref),
                InstanceType=variables['ManagementInstanceType'].ref,
                InstanceMonitoring=False,  # extra granularity not worth cost
                KeyName=If('SSHKeySpecified', variables['KeyName'].ref,
                           Ref('AWS::NoValue')),
                PlacementTenancy=variables['VpcInstanceTenancy'].ref,
                SecurityGroups=variables['VPNSecurityGroups'].ref,
                UserData=If(
                    'RHELUserData',
                    Base64(
                        Join(
                            '',
                            common_userdata_prefix + [
                                "yum_repos:\n",
                                "  epel:\n",
                                "    name: Extra Packages for $releasever - $basearch\n",  # noqa pylint: disable=line-too-long
                                "    baseurl: http://download.fedoraproject.org/pub/epel/7/$basearch\n",  # noqa pylint: disable=line-too-long
                                "    enabled: true\n",
                                "    failovermethod: priority\n",
                                "    gpgcheck: true\n",
                                "    gpgkey: https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-7\n",  # noqa pylint: disable=line-too-long
                                "packages:\n",
                                "  - awscli\n",
                                "  - python-pip\n",
                                "  - python2-boto\n",
                                "  - python2-boto3\n"
                            ])),
                    Base64(
                        Join(
                            '', common_userdata_prefix + [
                                "packages:\n", "  - awscli\n",
                                "  - python-pip\n", "  - python-boto\n",
                                "  - python-boto3\n"
                            ])))))

        template.add_resource(
            autoscaling.AutoScalingGroup(
                'VPNServerASG',
                MinSize=1,
                MaxSize=1,
                LaunchConfigurationName=Ref(vpnserverlaunchconfig),
                Tags=[
                    autoscaling.Tag(
                        'Name',
                        Join('-', [
                            variables['CustomerName'].ref, 'vpn',
                            variables['EnvironmentName'].ref
                        ]), True),
                    autoscaling.Tag('environment',
                                    variables['EnvironmentName'].ref, True),
                    autoscaling.Tag('customer', variables['CustomerName'].ref,
                                    True)
                ],
                VPCZoneIdentifier=If(
                    'PublicSubnetsOmitted',
                    GetAtt(subnetlookup.title, 'PublicSubnetList'),
                    variables['PublicSubnets'].ref)))
Example #12
0
    def add_resources(self):
        """Add resources to template."""
        template = self.template
        variables = self.get_variables()

        # IAM Instance Roles and Profiles
        commonpolicy = template.add_resource(
            iam.ManagedPolicy(
                'CommonPolicy',
                Description='Common instance policy; allows SSM management '
                            'and CloudWatch publishing.',
                Path='/',
                PolicyDocument=Policy(
                    Version='2012-10-17',
                    Statement=[
                        Statement(
                            Action=[awacs.ec2.DescribeInstances,
                                    awacs.ec2.DescribeTags,
                                    awacs.ec2.CreateTags],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[
                                awacs.logs.CreateLogGroup,
                                awacs.logs.CreateLogStream,
                                awacs.logs.PutLogEvents,
                                awacs.logs.DescribeLogStreams,
                                awacs.logs.DescribeLogGroups
                            ],
                            Effect=Allow,
                            Resource=[
                                'arn:aws:logs:*:*:*'
                            ]
                        ),
                        Statement(
                            Action=[awacs.cloudwatch.PutMetricData],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[awacs.aws.Action('s3', 'List*'),
                                    awacs.s3.GetBucketVersioning],
                            Effect=Allow,
                            Resource=[
                                variables['ChefBucketArn'].ref
                            ]
                        ),
                        Statement(
                            Action=[awacs.aws.Action('s3', 'Get*'),
                                    awacs.aws.Action('s3', 'List*')],
                            Effect=Allow,
                            Resource=[
                                Join('', [variables['ChefBucketArn'].ref,
                                          '/*'])
                            ]
                        ),
                        Statement(
                            Action=[awacs.aws.Action('s3', 'Get*'),
                                    awacs.aws.Action('s3', 'List*')],
                            Effect=Allow,
                            Resource=[
                                Join('', [variables['ChefDataBucketArn'].ref,
                                          '/all/*'])
                            ]
                        ),
                        Statement(  # Required for Jenkins invoked CodeBuild
                            Action=[awacs.s3.GetBucketVersioning],
                            Effect=Allow,
                            Resource=[variables['ChefDataBucketArn'].ref]
                        ),
                        Statement(
                            Action=[awacs.s3.ListBucket],
                            Effect=Allow,
                            Resource=[
                                Join('', [variables['ChefDataBucketArn'].ref])
                            ],
                            Condition=Condition(
                                StringLike('s3:prefix', ['', 'all/*'])
                            )
                        ),
                        Statement(  # For wildcard matching of parameters
                            Action=[awacs.ssm.DescribeParameters],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[awacs.ssm.GetParameters],
                            Effect=Allow,
                            Resource=[
                                Join(':', ['arn:aws:ssm',
                                           Ref('AWS::Region'),
                                           Ref('AWS::AccountId'),
                                           'parameter/all.*']),
                                Join(':', ['arn:aws:ssm',
                                           Ref('AWS::Region'),
                                           Ref('AWS::AccountId'),
                                           'parameter/all/*'])
                            ]
                        ),
                        # Platform downloading
                        Statement(
                            Action=[awacs.s3.ListBucket,
                                    awacs.s3.GetObject],
                            Effect=Allow,
                            Resource=[
                                'arn:aws:s3:::sturdy-platform*',
                                'arn:aws:s3:::onica-platform*',
                            ]
                        ),
                        # SSM
                        # Adapted from EC2RoleforSSM with the following changes
                        #   * Dropping the Put/Get S3 * access
                        #   * Dropping the GetParameters * access
                        Statement(
                            Action=[awacs.ssm.DescribeAssociation,
                                    awacs.aws.Action(
                                        'ssm',
                                        'GetDeployablePatchSnapshotForInstance'
                                    ),
                                    awacs.ssm.GetDocument,
                                    awacs.ssm.ListAssociations,
                                    awacs.ssm.ListInstanceAssociations,
                                    awacs.ssm.PutInventory,
                                    awacs.ssm.UpdateAssociationStatus,
                                    awacs.ssm.UpdateInstanceAssociationStatus,
                                    awacs.aws.Action(
                                        'ssm',
                                        'UpdateInstanceInformation')],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[awacs.aws.Action('ec2messages',
                                                     'AcknowledgeMessage'),
                                    awacs.aws.Action('ec2messages',
                                                     'DeleteMessage'),
                                    awacs.aws.Action('ec2messages',
                                                     'FailMessage'),
                                    awacs.aws.Action('ec2messages',
                                                     'GetEndpoint'),
                                    awacs.aws.Action('ec2messages',
                                                     'GetMessages'),
                                    awacs.aws.Action('ec2messages',
                                                     'SendReply')],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[awacs.ec2.DescribeInstanceStatus],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[awacs.ds.CreateComputer,
                                    awacs.ds.DescribeDirectories],
                            Effect=Allow,
                            Resource=['*']
                        ),
                        Statement(
                            Action=[awacs.s3.ListBucket],
                            Effect=Allow,
                            Resource=[
                                'arn:aws:s3:::amazon-ssm-packages-*'
                            ]
                        )
                    ]
                )
            )
        )
        commonrole = template.add_resource(
            iam.Role(
                'CommonRole',
                AssumeRolePolicyDocument=iam_policies.assumerolepolicy('ec2'),
                ManagedPolicyArns=[Ref(commonpolicy)],
                Path='/'
            )
        )
        commoninstanceprofile = template.add_resource(
            iam.InstanceProfile(
                'CommonInstanceProfile',
                Path='/',
                Roles=[Ref(commonrole)]
            )
        )
        template.add_output(
            Output(
                commonpolicy.title,
                Description='Common instance policy',
                Export=Export(
                    Sub('${AWS::StackName}-%s' % commonpolicy.title)
                ),
                Value=Ref(commonpolicy)
            )
        )
        template.add_output(
            Output(
                commonrole.title,
                Description='Common instance role',
                Export=Export(Sub('${AWS::StackName}-%s' % commonrole.title)),
                Value=Ref(commonrole)
            )
        )
        template.add_output(
            Output(
                commoninstanceprofile.title,
                Description='Common instance profile',
                Export=Export(
                    Sub('${AWS::StackName}-%s' % commoninstanceprofile.title)
                ),
                Value=Ref(commoninstanceprofile)
            )
        )