def _get_replicated_lambda_state_machine_role( self, remover_function, # type: Dict[str, Union[awslambda.Function, iam.Role, Any]] # noqa pylint: disable=line-too-long self_destruct_function # type: Dict[str, Union[awslambda.Function, iam.Role, Any]] # noqa pylint: disable=line-too-long ): # type (...) -> iam.Role entity = Join('.', ['states', Region, 'amazonaws.com']) return self.template.add_resource( iam.Role( 'StateMachineRole', AssumeRolePolicyDocument=make_simple_assume_policy(entity), Policies=[ iam.Policy( PolicyName="InvokeLambda", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.awslambda.InvokeFunction], Effect=Allow, Resource=[ remover_function.get_att('Arn'), self_destruct_function.get_att('Arn') ]) ])) ]))
def handle(self, chain_context): print("Adding source action %s." % self.action_name) template = chain_context.template policy_name = "CodeBuildPolicy%s" % chain_context.instance_name codebuild_policy = cumulus.policies.codebuild.get_policy_code_build_general_access( policy_name) role_name = "PipelineSourceRole%s" % self.action_name codebuild_role = iam.Role( role_name, Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codebuild.amazonaws.com")) ]), Policies=[codebuild_policy], ManagedPolicyArns=[ chain_context.metadata[META_PIPELINE_BUCKET_POLICY_REF] ]) source_action = SourceCodeCommitAction( Name=self.action_name, OutputArtifacts=[ codepipeline.OutputArtifacts(Name=self.output_artifact_name) ], # TODO: when parameters are figured out, inject tehm into the template here. Configuration={ "RepositoryName": Ref("RepositoryName"), "BranchName": Ref("RepositoryBranch"), }, ) template.add_resource(codebuild_role) found_pipelines = TemplateQuery.get_resource_by_type( template=chain_context.template, type_to_find=codepipeline.Pipeline) pipeline = found_pipelines[0] # Alternate way to get this # dummy = TemplateQuery.get_resource_by_title(chain_context.template, 'AppPipeline') stages = pipeline.Stages # type: list # TODO: find stage by name first_stage = stages[0] # TODO accept a parallel action to the previous action, and don't +1 here. first_stage.Actions.append(source_action) template.add_output( troposphere.Output("RepoName%s" % self.action_name, Value=Ref("RepositoryName"))) template.add_output( troposphere.Output("RepoBranch%s" % self.action_name, Value=Ref("RepositoryBranch")))
def add_cd_applications(self): cdapp_index = 0 for cdapp in self.aws['cd_application.names'].split(','): name, tags = self._name_tags('cd_application') cdapp_name = name + str(cdapp_index) self.cd_application = self.t.add_resource( codedeploy.Application( cdapp_name, ApplicationName=cdapp, # Doesn't support: Tags=Tags(**tags), )) cdapp_output_name = "CDApp" + str(cdapp_index) self.t.add_output(Output( cdapp_output_name, Value=Ref(self.cd_application) )) cd_role_name = cdapp + "CDRole" self.cd_role = self.t.add_resource( iam.Role( cd_role_name, AssumeRolePolicyDocument=Policy( Statement=[ Statement( Action=[awacs.sts.AssumeRole], Effect=Allow, Principal=Principal('Service', 'codedeploy.amazonaws.com'), Sid=cd_role_name, # redundant? ), ], ), ManagedPolicyArns=['arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'], Path='/', )) cd_role_output_name = "CDRole" + str(cdapp_index) self.t.add_output(Output( cd_role_output_name, Value=Ref(self.cd_role) )) cd_iam_user_name = cdapp + "CDUser" self.cd_iam_user = self.t.add_resource( iam.User( cd_iam_user_name, # Can't attach policy here, # must create policy and attach to user from app stack # ManagedPolicyArns=[Ref(self.cd_iam_user_policy.name)] )) cd_iam_user_output_name = "CDUser" + str(cdapp_index) self.t.add_output(Output( cd_iam_user_output_name, Value=Ref(self.cd_iam_user) )) cdapp_index += 1
def handle(self, chain_context): print("Adding action %s to Stage." % self.action_name) suffix = "%s%s" % (self.stage_name_to_add, self.action_name) policy_name = "LambdaPolicy%s" % chain_context.instance_name role_name = "LambdaRole%s" % suffix lambda_role = iam.Role( role_name, Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "lambda.amazonaws.com")) ]), Policies=[ # TODO: new policy cumulus.policies.codebuild. get_policy_code_build_general_access(policy_name) ], ManagedPolicyArns=[ chain_context.metadata[META_PIPELINE_BUCKET_POLICY_REF] ]) lambda_action = cumulus.types.codebuild.buildaction.LambdaAction( Name=self.action_name, InputArtifacts=[ codepipeline.InputArtifacts(Name=self.input_artifact_name) ], Configuration={ 'FunctionName': self.function_name, }, RunOrder="1") if self.user_parameters: lambda_action.Configuration[ 'UserParameters'] = self.user_parameters chain_context.template.add_resource(lambda_role) stage = cumulus.util.template_query.TemplateQuery.get_pipeline_stage_by_name( template=chain_context.template, stage_name=self.stage_name_to_add, ) # TODO accept a parallel action to the previous action, and don't +1 here. last_run_order = len(stage.Actions) next_run_order = last_run_order if self.is_parallel_task else last_run_order + 1 lambda_action.RunOrder = next_run_order stage.Actions.append(lambda_action)
def add_lambda_execution_role( self, name='LambdaExecutionRole', # type: str function_name='' # type: str ): # noqa: E124 # type: (...) -> iam.Role """Create the Lambda@Edge execution role.""" variables = self.get_variables() lambda_resource = Join('', [ 'arn:', Partition, ':logs:*:', AccountId, ':log-group:/aws/lambda/', StackName, '-%s-*' % function_name ]) edge_resource = Join('', [ 'arn:', Partition, ':logs:*:', AccountId, ':log-group:/aws/lambda/*.', StackName, '-%s-*' % function_name, ]) return self.template.add_resource( iam.Role( name, AssumeRolePolicyDocument=make_simple_assume_policy( 'lambda.amazonaws.com', 'edgelambda.amazonaws.com'), PermissionsBoundary=(variables['RoleBoundaryArn'] if self.role_boundary_specified else NoValue), Policies=[ iam.Policy(PolicyName="LambdaLogCreation", PolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ Statement(Action=[ awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, awacs.logs.PutLogEvents ], Effect=Allow, Resource=[ lambda_resource, edge_resource ]) ])), ], ))
def get_default_code_build_role(self, chain_context, policy_name, role_name): codebuild_role = iam.Role( role_name, Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codebuild.amazonaws.com")) ]), Policies=[ cumulus.policies.codebuild. get_policy_code_build_general_access(policy_name) ], ManagedPolicyArns=[ chain_context.metadata[META_PIPELINE_BUCKET_POLICY_REF] ]) return codebuild_role
def handle(self, chain_context): print("Adding approval action %s." % self.action_name) policy_name = "CodeBuildPolicy%sStage" % chain_context.instance_name role_name = "CodeBuildRole%sStage" % self.action_name codebuild_role = iam.Role( role_name, Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codebuild.amazonaws.com")) ]), Policies= [ # TODO: policy 'could' be reduced to executing cfn approvals cumulus.policies.codebuild. get_policy_code_build_general_access(policy_name) ], ManagedPolicyArns=[ chain_context.metadata[META_PIPELINE_BUCKET_POLICY_REF] ]) approval_action = cumulus.types.codebuild.buildaction.ApprovalAction( Name=self.action_name, RunOrder="1") chain_context.template.add_resource(codebuild_role) template = chain_context.template stage_to_add = self.stage_name_to_add stage = cumulus.util.template_query.TemplateQuery.get_pipeline_stage_by_name( template=template, stage_name=stage_to_add, ) next_run_order = len(stage.Actions) + 1 approval_action.RunOrder = next_run_order stage.Actions.append(approval_action)
def _get_replicated_lambda_state_machine_role( self, remover_function, # type: Dict[str, Union[awslambda.Function, iam.Role, Any]] self_destruct_function, # type: Dict[str, Union[awslambda.Function, iam.Role, Any]] ): # type (...) -> iam.Role variables = self.get_variables() entity = Join(".", ["states", Region, "amazonaws.com"]) return self.template.add_resource( iam.Role( "StateMachineRole", AssumeRolePolicyDocument=make_simple_assume_policy(entity), PermissionsBoundary=( variables["RoleBoundaryArn"] if self.role_boundary_specified else NoValue ), Policies=[ iam.Policy( PolicyName="InvokeLambda", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.awslambda.InvokeFunction], Effect=Allow, Resource=[ remover_function.get_att("Arn"), self_destruct_function.get_att("Arn"), ], ) ], ), ) ], ) )
def _get_replicated_lambda_state_machine_role(self, remover_function, # type: Dict[str, Union[awslambda.Function, iam.Role, Any]] # noqa pylint: disable=line-too-long self_destruct_function # type: Dict[str, Union[awslambda.Function, iam.Role, Any]] # noqa pylint: disable=line-too-long # TODO remove after dropping python 2 ): # pylint: disable=bad-continuation # type (...) -> iam.Role variables = self.get_variables() entity = Join('.', ['states', Region, 'amazonaws.com']) return self.template.add_resource( iam.Role( 'StateMachineRole', AssumeRolePolicyDocument=make_simple_assume_policy(entity), PermissionsBoundary=( variables['RoleBoundaryArn'] if self.role_boundary_specified else NoValue ), Policies=[ iam.Policy( PolicyName="InvokeLambda", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.awslambda.InvokeFunction], Effect=Allow, Resource=[ remover_function.get_att('Arn'), self_destruct_function.get_att('Arn') ] ) ] ) ) ] ) )
def add_role(self): name, tags = self._name_tags('role') self.role = self.t.add_resource( iam.Role( name, AssumeRolePolicyDocument=Policy( Statement=[ Statement( Action=[awacs.sts.AssumeRole], Effect=Allow, Principal=Principal('Service', 'ec2.amazonaws.com'), Sid=name, # redundant? ), ], ), ManagedPolicyArns=['arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess'], Path='/', Policies=[iam.Policy( name + 'Policy', PolicyDocument=Policy( Statement=[ Statement( Action=[ awacs.s3.GetObject, awacs.s3.ListBucket, ], Effect=Allow, Resource=[awacs.s3.ARN(self.aws['s3.bucket']), awacs.s3.ARN(self.aws['s3.bucket'] + '/*')], ), ], ), PolicyName=name + 'Policy', )], # Doesn't support: Tags=Tags(**tags) ))
def handle(self, chain_context): print("Adding action %s Stage." % self.action_name) full_action_name = "%s%s" % (self.stage_name_to_add, self.action_name) policy_name = "%sCodeBuildPolicy" % chain_context.instance_name role_name = "CodeBuildRole%s" % full_action_name codebuild_role = iam.Role( role_name, Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codebuild.amazonaws.com")) ]), Policies=[ cumulus.policies.codebuild. get_policy_code_build_general_access(policy_name) ], ManagedPolicyArns=[ chain_context.metadata[META_PIPELINE_BUCKET_POLICY_REF] ]) if not self.environment: self.environment = codebuild.Environment( ComputeType='BUILD_GENERAL1_SMALL', Image='aws/codebuild/python:2.7.12', Type='LINUX_CONTAINER', EnvironmentVariables=[ # TODO: allow these to be injectable, or just the whole environment? { 'Name': 'PIPELINE_BUCKET', 'Value': chain_context.metadata[META_PIPELINE_BUCKET_NAME] } ], ) project = self.create_project( chain_context=chain_context, codebuild_role=codebuild_role, codebuild_environment=self.environment, name=full_action_name, ) code_build_action = cumulus.types.codebuild.buildaction.CodeBuildAction( Name=self.action_name, InputArtifacts=[ codepipeline.InputArtifacts(Name=self.input_artifact_name) ], Configuration={'ProjectName': Ref(project)}, RunOrder="1") chain_context.template.add_resource(codebuild_role) chain_context.template.add_resource(project) template = chain_context.template stage = cumulus.util.template_query.TemplateQuery.get_pipeline_stage_by_name( template=template, stage_name=self.stage_name_to_add, ) # TODO accept a parallel action to the previous action, and don't +1 here. next_run_order = len(stage.Actions) + 1 code_build_action.RunOrder = next_run_order stage.Actions.append(code_build_action)
def handle(self, chain_context): """ This step adds in the shell of a pipeline. * s3 bucket * policies for the bucket and pipeline * your next step in the chain MUST be a source stage :param chain_context: :return: """ if self.create_bucket: pipeline_bucket = Bucket( "PipelineBucket%s" % chain_context.instance_name, BucketName=self.bucket_name, VersioningConfiguration=VersioningConfiguration( Status="Enabled")) chain_context.template.add_resource(pipeline_bucket) default_bucket_policies = self.get_default_bucket_policy_statements( self.bucket_name) if self.bucket_policy_statements: bucket_access_policy = self.get_bucket_policy( pipeline_bucket=self.bucket_name, bucket_policy_statements=self.bucket_policy_statements, ) chain_context.template.add_resource(bucket_access_policy) pipeline_bucket_access_policy = iam.ManagedPolicy( "PipelineBucketAccessPolicy", Path='/managed/', PolicyDocument=awacs.aws.PolicyDocument( Version="2012-10-17", Id="bucket-access-policy%s" % chain_context.instance_name, Statement=default_bucket_policies)) chain_context.metadata[cumulus.steps.dev_tools. META_PIPELINE_BUCKET_NAME] = self.bucket_name chain_context.metadata[ cumulus.steps.dev_tools.META_PIPELINE_BUCKET_POLICY_REF] = Ref( pipeline_bucket_access_policy) # TODO: this can be cleaned up by using a policytype and passing in the pipeline role it should add itself to. pipeline_policy = iam.Policy( PolicyName="%sPolicy" % self.name, PolicyDocument=awacs.aws.PolicyDocument( Version="2012-10-17", Id="PipelinePolicy", Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, # TODO: actions here could be limited more Action=[awacs.aws.Action("s3", "*")], Resource=[ troposphere.Join( '', [awacs.s3.ARN(), self.bucket_name, "/*"]), troposphere.Join('', [ awacs.s3.ARN(), self.bucket_name, ]), ], ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.aws.Action("kms", "*")], Resource=['*'], ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.aws.Action("cloudformation", "*"), awacs.aws.Action("codebuild", "*"), ], # TODO: restrict more accurately Resource=["*"]), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.codecommit.GetBranch, awacs.codecommit.GetCommit, awacs.codecommit.UploadArchive, awacs.codecommit.GetUploadArchiveStatus, awacs.codecommit.CancelUploadArchive ], Resource=["*"]), awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.iam.PassRole], Resource=["*"]), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.aws.Action("lambda", "*")], Resource=["*"]) ], )) pipeline_service_role = iam.Role( "PipelineServiceRole", Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codepipeline.amazonaws.com")) ]), Policies=[pipeline_policy] + self.pipeline_policies) generic_pipeline = codepipeline.Pipeline( "Pipeline", RoleArn=troposphere.GetAtt(pipeline_service_role, "Arn"), Stages=[], ArtifactStore=codepipeline.ArtifactStore( Type="S3", Location=self.bucket_name, ) # TODO: optionally add kms key here ) if self.bucket_kms_key_arn: encryption_config = codepipeline.EncryptionKey( "ArtifactBucketKmsKey", Id=self.bucket_kms_key_arn, Type='KMS', ) generic_pipeline.ArtifactStore.EncryptionKey = encryption_config pipeline_output = troposphere.Output( "PipelineName", Description="Code Pipeline", Value=Ref(generic_pipeline), ) pipeline_bucket_output = troposphere.Output( "PipelineBucket", Description="Name of the input artifact bucket for the pipeline", Value=self.bucket_name, ) chain_context.template.add_resource(pipeline_bucket_access_policy) chain_context.template.add_resource(pipeline_service_role) chain_context.template.add_resource(generic_pipeline) chain_context.template.add_output(pipeline_output) chain_context.template.add_output(pipeline_bucket_output)
def add_lambda_execution_role(self, name: str = "LambdaExecutionRole", function_name: str = "") -> iam.Role: """Create the Lambda@Edge execution role. Args: name: Name for the Lambda Execution Role. function_name: Name of the Lambda Function the Role will be attached to. """ lambda_resource = Join( "", [ "arn:", Partition, ":logs:*:", AccountId, ":log-group:/aws/lambda/", StackName, "-%s-*" % function_name, ], ) edge_resource = Join( "", [ "arn:", Partition, ":logs:*:", AccountId, ":log-group:/aws/lambda/*.", StackName, "-%s-*" % function_name, ], ) return self.template.add_resource( iam.Role( name, AssumeRolePolicyDocument=make_simple_assume_policy( "lambda.amazonaws.com", "edgelambda.amazonaws.com"), PermissionsBoundary=(self.variables["RoleBoundaryArn"] if self.role_boundary_specified else NoValue), Policies=[ iam.Policy( PolicyName="LambdaLogCreation", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[ awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, awacs.logs.PutLogEvents, ], Effect=Allow, Resource=[lambda_resource, edge_resource], ) ], ), ), ], ))
def _get_replicated_lambda_remover_lambda(self): # type: () -> Dict[str, Any] res = {} variables = self.get_variables() res["role"] = self.template.add_resource( iam.Role( "ReplicatedLambdaRemoverRole", AssumeRolePolicyDocument=make_simple_assume_policy( "lambda.amazonaws.com" ), PermissionsBoundary=( variables["RoleBoundaryArn"] if self.role_boundary_specified else NoValue ), Policies=[ iam.Policy( PolicyName="LambdaLogCreation", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[ awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, awacs.logs.PutLogEvents, ], Effect=Allow, Resource=[ Join( "", [ "arn:", Partition, ":logs:*:", AccountId, ":log-group:/aws/lambda/", StackName, "-ReplicatedLambdaRemover-*", ], ) ], ) ], ), ), iam.Policy( PolicyName="DeleteLambda", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.awslambda.DeleteFunction], Effect=Allow, Resource=self.get_variables()["function_arns"], ) ], ), ), ], ) ) self.template.add_output( Output( "ReplicatedLambdaRemoverRole", Description="The name of the Replicated Lambda Remover Role", Value=res["role"].ref(), ) ) res["function"] = self.template.add_resource( awslambda.Function( "ReplicatedLambdaRemover", Code=awslambda.Code( ZipFile=read_value_from_path( "file://" + os.path.join( os.path.dirname(__file__), "templates/replicated_lambda_remover.template.py", ) ) ), Description="Checks for Replicated Lambdas created during the main stack and " "deletes them when they are ready.", Handler="index.handler", Role=res["role"].get_att("Arn"), Runtime="python3.7", ) ) self.template.add_output( Output( "ReplicatedLambdaRemoverArn", Description="The ARN of the Replicated Function", Value=res["function"].get_att("Arn"), ) ) return res
def _get_self_destruct(self, replicated_lambda_remover): # type: (Dict[str, Union[awslambda.Function, Any]]) -> Dict[str, Any] res = {} variables = self.get_variables() res["role"] = self.template.add_resource( iam.Role( "SelfDestructRole", AssumeRolePolicyDocument=make_simple_assume_policy( "lambda.amazonaws.com" ), PermissionsBoundary=( variables["RoleBoundaryArn"] if self.role_boundary_specified else NoValue ), Policies=[ iam.Policy( PolicyName="LambdaLogCreation", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[ awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, awacs.logs.PutLogEvents, ], Effect=Allow, Resource=[ Join( "", [ "arn:", Partition, ":logs:*:", AccountId, ":log-group:/aws/lambda/", StackName, "-SelfDestruct-*", ], ) ], ) ], ), ), iam.Policy( PolicyName="DeleteStateMachine", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.states.DeleteStateMachine], Effect=Allow, Resource=[ # StateMachine Join( "", [ "arn:", Partition, ":states:", Region, ":", AccountId, ":stateMachine:StaticSiteCleanup-", variables["stack_name"], ], ) ], ) ], ), ), iam.Policy( PolicyName="DeleteRolesAndPolicies", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[ awacs.iam.DeleteRolePolicy, awacs.iam.DeleteRole, ], Effect=Allow, Resource=[ Join( "", [ "arn:", Partition, ":iam::", AccountId, ":role/", StackName, "-*", ], ), ], ) ], ), ), iam.Policy( PolicyName="DeleteLambdas", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.awslambda.DeleteFunction], Effect=Allow, Resource=[ Join( "", [ "arn:", Partition, ":lambda:", Region, ":", AccountId, ":function:%s-SelfDestruct-*" % (variables["stack_name"]), ], ), replicated_lambda_remover["function"].get_att( "Arn" ), ], ) ], ), ), iam.Policy( PolicyName="DeleteStack", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.cloudformation.DeleteStack], Effect=Allow, Resource=[ Join( "", [ "arn:", Partition, ":cloudformation:", Region, ":", AccountId, ":stack/%s/*" % (variables["stack_name"]), ], ) ], ) ], ), ), ], ) ) self.template.add_output( Output( "SelfDestructLambdaRole", Description="The name of the Self Destruct Role", Value=res["role"].ref(), ) ) res["function"] = self.template.add_resource( awslambda.Function( "SelfDestruct", Code=awslambda.Code( ZipFile=read_value_from_path( "file://" + os.path.join( os.path.dirname(__file__), "templates/self_destruct.template.py", ) ) ), Description="Issues a Delete Stack command to the Cleanup stack", Handler="index.handler", Role=res["role"].get_att("Arn"), Runtime="python3.7", ) ) self.template.add_output( Output( "SelfDestructLambdaArn", Description="The ARN of the Replicated Function", Value=res["function"].get_att("Arn"), ) ) return res
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), ))
def generate(template): cloudformation_deploy_policy = template.add_resource( iam.ManagedPolicy( 'CFDeployPolicy', ManagedPolicyName='CloudFormationDeployAccess', Description='Allows CloudFormation to deploy the application.', PolicyDocument=awacs.aws.PolicyDocument( Version='2012-10-17', Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.aws.Action("*")], Resource=["*"], ), ]))) cloudformation_deploy_role = template.add_resource( iam.Role('CFDeployRole', DependsOn=[cloudformation_deploy_policy], RoleName='CloudFormationDeploy', MaxSessionDuration=3600, ManagedPolicyArns=[ Ref(cloudformation_deploy_policy), "arn:aws:iam::aws:policy/IAMReadOnlyAccess" ], AssumeRolePolicyDocument=awacs.aws.PolicyDocument(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( "Service", "cloudformation.amazonaws.com")) ]))) deploy_policy = template.add_resource( iam.ManagedPolicy( 'DeployPolicy', DependsOn=[cloudformation_deploy_role], ManagedPolicyName='DeployAccess', Description='Allows access for deploying the application.', PolicyDocument=awacs.aws.PolicyDocument( Version='2012-10-17', Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.aws.Action("cloudformation", "*")], Resource=["*"], ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.aws.Action("s3", "Get*"), awacs.s3.PutObject, awacs.s3.ListBucket, ], Resource=["*"], ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.iam.PassRole], Resource=[GetAtt(cloudformation_deploy_role, "Arn")]) ]))) role_deploy = template.add_resource( iam.Role( 'RoleDeploy', DependsOn=[deploy_policy], RoleName='Deploy', MaxSessionDuration=3600, ManagedPolicyArns=[Ref(deploy_policy)], AssumeRolePolicyDocument=awacs.aws.PolicyDocument( Version='2012-10-17', Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( "AWS", Sub("arn:aws:iam::${AWS::AccountId}:user/DeploymentRobot" ))) ])))
def handle(self, chain_context): print("Adding source action %s." % self.action_name) template = chain_context.template policy_name = "CodeBuildPolicy%s" % chain_context.instance_name codebuild_policy = cumulus.policies.codebuild.get_policy_code_build_general_access( policy_name) role_name = "PipelineSourceRole%s" % self.action_name codebuild_role = iam.Role( role_name, Path="/", AssumeRolePolicyDocument=awacs.aws.Policy(Statement=[ awacs.aws.Statement(Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codebuild.amazonaws.com")) ]), Policies=[codebuild_policy], ManagedPolicyArns=[ chain_context.metadata[META_PIPELINE_BUCKET_POLICY_REF] ]) source_action = SourceS3Action( Name=self.action_name, OutputArtifacts=[ codepipeline.OutputArtifacts(Name=self.output_artifact_name) ], Configuration={ "S3Bucket": self.s3_bucket_name, "S3ObjectKey": self.s3_object_key, }, ) # TODO: support CFN params here. Use conditionals. Set to NoValue instead of None, using the same logic as below if self.poll_for_source_changes is not None: # if it's none - we shouldn't touch this. source_action.Configuration[ 'PollForSourceChanges'] = self.poll_for_source_changes template.add_resource(codebuild_role) found_pipelines = TemplateQuery.get_resource_by_type( template=chain_context.template, type_to_find=codepipeline.Pipeline) pipeline = found_pipelines[0] # Alternate way to get this # dummy = TemplateQuery.get_resource_by_title(chain_context.template, 'AppPipeline') stages = pipeline.Stages # type: list # TODO: find stage by name first_stage = stages[0] # TODO accept a parallel action to the previous action, and don't +1 here. first_stage.Actions.append(source_action) template.add_output( troposphere.Output( "PipelineBucket%s" % self.action_name, Value=self.s3_bucket_name, Description="A pipeline source bucket", )) template.add_output( troposphere.Output( "PipelineTriggerObject%s" % self.action_name, Value=self.s3_object_key, Description="An s3 object key in the pipeline bucket " "that will trigger the pipeline", ))
def create_template(self) -> None: """Create template.""" template = self.template bucket_arn = Sub("arn:aws:s3:::${CFNginBucket}*") objects_arn = Sub("arn:aws:s3:::${CFNginBucket}*/*") cloudformation_scope = Sub( "arn:aws:cloudformation:*:${AWS::AccountId}:stack/${Namespace}-*") changeset_scope = "*" # This represents the precise IAM permissions that cfngin itself # needs. cfngin_policy = iam.Policy( PolicyName="CFNgin", PolicyDocument=Policy(Statement=[ Statement( Effect="Allow", Resource=["*"], Action=[awacs.s3.ListAllMyBuckets], ), Statement( Effect="Allow", Resource=[bucket_arn], Action=[ awacs.s3.ListBucket, awacs.s3.GetBucketLocation, awacs.s3.CreateBucket, awacs.s3.DeleteBucket, ], ), Statement( Effect="Allow", Resource=[bucket_arn], Action=[ awacs.s3.GetObject, awacs.s3.GetObjectAcl, awacs.s3.PutObject, awacs.s3.PutObjectAcl, ], ), Statement( Effect="Allow", Resource=[objects_arn], Action=[awacs.s3.DeleteObject], ), Statement( Effect="Allow", Resource=[changeset_scope], Action=[ awacs.cloudformation.DescribeChangeSet, awacs.cloudformation.ExecuteChangeSet, awacs.cloudformation.DeleteChangeSet, ], ), Statement( Effect="Deny", Resource=[Ref("AWS::StackId")], Action=[awacs.cloudformation.Action("*")], ), Statement( Effect="Allow", Resource=[cloudformation_scope], Action=[ awacs.cloudformation.GetTemplate, awacs.cloudformation.CreateChangeSet, awacs.cloudformation.DeleteChangeSet, awacs.cloudformation.DeleteStack, awacs.cloudformation.CreateStack, awacs.cloudformation.UpdateStack, awacs.cloudformation.SetStackPolicy, awacs.cloudformation.DescribeStacks, awacs.cloudformation.DescribeStackEvents, ], ), ]), ) principal = AWSPrincipal(Ref("AWS::AccountId")) role = template.add_resource( iam.Role( "FunctionalTestRole", AssumeRolePolicyDocument=Policy(Statement=[ Statement( Effect="Allow", Action=[awacs.sts.AssumeRole], Principal=principal, ) ]), Policies=[cfngin_policy], )) assumerole_policy = iam.Policy( PolicyName="AssumeRole", PolicyDocument=Policy(Statement=[ Statement( Effect="Allow", Resource=[GetAtt(role, "Arn")], Action=[awacs.sts.AssumeRole], ) ]), ) user = template.add_resource( iam.User("FunctionalTestUser", Policies=[cfngin_policy, assumerole_policy])) key = template.add_resource( iam.AccessKey("FunctionalTestKey", Serial=1, UserName=Ref(user))) template.add_output(Output("User", Value=Ref(user))) template.add_output(Output("AccessKeyId", Value=Ref(key))) template.add_output( Output("SecretAccessKey", Value=GetAtt("FunctionalTestKey", "SecretAccessKey"))) template.add_output( Output("FunctionalTestRole", Value=GetAtt(role, "Arn")))
def _get_self_destruct(self, replicated_lambda_remover): # type: (Dict[str, Union[awslambda.Function, Any]]) -> Dict[str, Any] res = {} variables = self.get_variables() res['role'] = self.template.add_resource( iam.Role( 'SelfDestructRole', AssumeRolePolicyDocument=make_simple_assume_policy( 'lambda.amazonaws.com'), Policies=[ iam.Policy(PolicyName="LambdaLogCreation", PolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ Statement( Action=[ awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, awacs.logs.PutLogEvents ], Effect=Allow, Resource=[ Join('', [ 'arn:', Partition, ':logs:*:', AccountId, ':log-group:/aws/lambda/', StackName, '-SelfDestruct-*' ]) ]) ])), iam.Policy( PolicyName="DeleteStateMachine", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.states.DeleteStateMachine], Effect=Allow, Resource=[ # StateMachine Join('', [ 'arn:', Partition, ':states:', Region, ':', AccountId, ':stateMachine:StaticSiteCleanup-', variables['stack_name'] ]) ]) ])), iam.Policy(PolicyName="DeleteRolesAndPolicies", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[ awacs.iam.DeleteRolePolicy, awacs.iam.DeleteRole ], Effect=Allow, Resource=[ Join('', [ 'arn:', Partition, ':iam::', AccountId, ':role/', StackName, '-*' ]), ]) ])), iam.Policy( PolicyName="DeleteLambdas", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.awslambda.DeleteFunction], Effect=Allow, Resource=[ Join('', [ 'arn:', Partition, ':lambda:', Region, ':', AccountId, ':function:%s-SelfDestruct-*' % (variables['stack_name']) ]), replicated_lambda_remover['function']. get_att('Arn') ]) ])), iam.Policy( PolicyName="DeleteStack", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement( Action=[awacs.cloudformation.DeleteStack], Effect=Allow, Resource=[ Join('', [ 'arn:', Partition, ':cloudformation:', Region, ':', AccountId, ':stack/%s/*' % (variables['stack_name']) ]) ]) ])) ], )) self.template.add_output( Output('SelfDestructLambdaRole', Description='The name of the Self Destruct Role', Value=res['role'].ref())) res['function'] = self.template.add_resource( awslambda.Function( 'SelfDestruct', Code=awslambda.Code(ZipFile=read_value_from_path( 'file://' + os.path.join(os.path.dirname(__file__), 'templates/self_destruct.template.py'))), Description= "Issues a Delete Stack command to the Cleanup stack", Handler='index.handler', Role=res['role'].get_att('Arn'), Runtime='python3.7')) self.template.add_output( Output('SelfDestructLambdaArn', Description='The ARN of the Replicated Function', Value=res['function'].get_att('Arn'))) return res
), Roles=[ Ref("NucleusRole"), Ref("StatefulBodyRole"), Ref("StatelessBodyRole"), Ref("MembraneRole") ] )) NucleusRole = t.add_resource(iam.Role( "NucleusRole", AssumeRolePolicyDocument=awacs.aws.Policy( Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, Principal=awacs.aws.Principal(principal='Service', resources=['ec2.amazonaws.com']), Action=[awacs.sts.AssumeRole] ) ] ), Path='/' )) NucleusInstanceProfile = t.add_resource(iam.InstanceProfile( "NucleusInstanceProfile", Path="/", Roles=[Ref(NucleusRole)], )) MembraneRole = t.add_resource(iam.Role( "MembraneRole",
def _get_replicated_lambda_remover_lambda(self): # type: () -> Dict[str, Any] res = {} res['role'] = self.template.add_resource( iam.Role( 'ReplicatedLambdaRemoverRole', AssumeRolePolicyDocument=make_simple_assume_policy( 'lambda.amazonaws.com'), Policies=[ iam.Policy(PolicyName="LambdaLogCreation", PolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ Statement( Action=[ awacs.logs.CreateLogGroup, awacs.logs.CreateLogStream, awacs.logs.PutLogEvents ], Effect=Allow, Resource=[ Join('', [ 'arn:', Partition, ':logs:*:', AccountId, ':log-group:/aws/lambda/', StackName, '-ReplicatedLambdaRemover-*' ]) ]) ])), iam.Policy(PolicyName="DeleteLambda", PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=[ Statement(Action=[ awacs.awslambda.DeleteFunction ], Effect=Allow, Resource=self.get_variables() ['function_arns']) ])) ], )) self.template.add_output( Output( 'ReplicatedLambdaRemoverRole', Description='The name of the Replicated Lambda Remover Role', Value=res['role'].ref())) res['function'] = self.template.add_resource( awslambda.Function( 'ReplicatedLambdaRemover', Code=awslambda.Code( ZipFile=read_value_from_path('file://' + os.path.join( os.path.dirname(__file__), 'templates/replicated_lambda_remover.template.py'))), Description= "Checks for Replicated Lambdas created during the main stack and " "deletes them when they are ready.", Handler='index.handler', Role=res['role'].get_att('Arn'), Runtime='python3.7')) self.template.add_output( Output('ReplicatedLambdaRemoverArn', Description='The ARN of the Replicated Function', Value=res['function'].get_att('Arn'))) return res
def get_default_pipeline_role(self): # TODO: this can be cleaned up by using a policytype and passing in the pipeline role it should add itself to. pipeline_policy = iam.Policy( PolicyName="%sPolicy" % self.name, PolicyDocument=awacs.aws.PolicyDocument( Version="2012-10-17", Id="PipelinePolicy", Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, # TODO: actions here could be limited more Action=[awacs.aws.Action("s3", "*")], Resource=[ troposphere.Join('', [ awacs.s3.ARN(), self.bucket_name, "/*" ]), troposphere.Join('', [ awacs.s3.ARN(), self.bucket_name, ]), ], ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.aws.Action("kms", "*")], Resource=['*'], ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.aws.Action("cloudformation", "*"), awacs.aws.Action("codebuild", "*"), ], # TODO: restrict more accurately Resource=["*"] ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.codecommit.GetBranch, awacs.codecommit.GetCommit, awacs.codecommit.UploadArchive, awacs.codecommit.GetUploadArchiveStatus, awacs.codecommit.CancelUploadArchive ], Resource=["*"] ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.iam.PassRole ], Resource=["*"] ), awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[ awacs.aws.Action("lambda", "*") ], Resource=["*"] ), ], ) ) pipeline_service_role = iam.Role( "PipelineServiceRole", Path="/", AssumeRolePolicyDocument=awacs.aws.Policy( Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, Action=[awacs.sts.AssumeRole], Principal=awacs.aws.Principal( 'Service', "codepipeline.amazonaws.com" ) )] ), Policies=[pipeline_policy] + self.pipeline_policies ) return pipeline_service_role