def _add_service(self, service_name, config): env_config = build_config( self.env, self.application_name, self.env_sample_file_path ) container_definition_arguments = { "Environment": [ Environment(Name=k, Value=v) for (k, v) in env_config ], "Name": service_name + "Container", "Image": self.ecr_image_uri + ':' + self.current_version, "Essential": 'true', "LogConfiguration": self._gen_log_config(service_name), "MemoryReservation": int(config['memory_reservation']), "Cpu": 0 } if 'http_interface' in config: container_definition_arguments['PortMappings'] = [ PortMapping( ContainerPort=int( config['http_interface']['container_port'] ) ) ] if config['command'] is not None: container_definition_arguments['Command'] = [config['command']] cd = ContainerDefinition(**container_definition_arguments) task_role = self.template.add_resource(Role( service_name + "Role", AssumeRolePolicyDocument=PolicyDocument( Statement=[ Statement( Effect=Allow, Action=[AssumeRole], Principal=Principal("Service", ["ecs-tasks.amazonaws.com"]) ) ] ) )) td = TaskDefinition( service_name + "TaskDefinition", Family=service_name + "Family", ContainerDefinitions=[cd], TaskRoleArn=Ref(task_role) ) self.template.add_resource(td) desired_count = self._get_desired_task_count_for_service(service_name) deployment_configuration = DeploymentConfiguration( MinimumHealthyPercent=100, MaximumPercent=200 ) if 'http_interface' in config: alb, lb, service_listener = self._add_alb(cd, service_name, config) svc = Service( service_name, LoadBalancers=[lb], Cluster=self.cluster_name, Role=Ref(self.ecs_service_role), TaskDefinition=Ref(td), DesiredCount=desired_count, DependsOn=service_listener.title, PlacementStrategies=self.PLACEMENT_STRATEGIES ) self.template.add_output( Output( service_name + 'EcsServiceName', Description='The ECS name which needs to be entered', Value=GetAtt(svc, 'Name') ) ) self.template.add_output( Output( service_name + "URL", Description="The URL at which the service is accessible", Value=Sub("https://${" + alb.name + ".DNSName}") ) ) self.template.add_resource(svc) else: svc = Service( service_name, Cluster=self.cluster_name, TaskDefinition=Ref(td), DesiredCount=desired_count, DeploymentConfiguration=deployment_configuration, PlacementStrategies=self.PLACEMENT_STRATEGIES ) self.template.add_output( Output( service_name + 'EcsServiceName', Description='The ECS name which needs to be entered', Value=GetAtt(svc, 'Name') ) ) self.template.add_resource(svc) self._add_service_alarms(svc)
# Example taken from AWS docs: # http://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html from awacs.aws import Allow from awacs.aws import Policy, Statement import awacs.ecr as ecr # AmazonEC2ContainerRegistryReadOnly pd = Policy(Statement=[ Statement(Effect=Allow, Action=[ ecr.GetAuthorizationToken, ecr.BatchCheckLayerAvailability, ecr.GetDownloadUrlForLayer, ecr.GetRepositoryPolicy, ecr.DescribeRepositories, ecr.ListImages, ecr.BatchGetImage, ], Resource=['*']) ]) print(pd.to_json())
FromPort='443', ToPort='443', CidrIp=quad_zero_ip), SecurityGroupRule( # Web Interface Kibana IpProtocol='tcp', FromPort='5601', ToPort='5601', CidrIp=quad_zero_ip) ])) jenkinsRole = t.add_resource( Role( "jenkinsRole", AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[AssumeRole], Principal=Principal("Service", ["ec2.amazonaws.com"])) ]), Path="/", Policies=[ iam.Policy( PolicyName="JenkinsPolicies", PolicyDocument=Policy(Statement=[ Statement( Effect=Allow, Action=[ CancelUpdateStack, CreateStack, ListStackResources, DescribeStackEvents, UpdateStack ], Resource=[Ref('AWS::StackId')], ),
def codebuild_policy_document(input_kwargs, build_data): user = "******" account = build_data["account_id"] pd = PolicyDocument( Version="2012-10-17", Id="Codebuild-Permissions", Statement=[ Statement( Effect=Allow, Principal=Principal("AWS", [IAM_ARN(user, '', account)]), Action=[ codebuild.BatchDeleteBuilds, codebuild.BatchGetBuilds, codebuild.BatchGetProjects, codebuild.BatchGetReportGroups, codebuild.BatchGetReports, codebuild.BatchPutTestCases, codebuild.CreateProject, codebuild.CreateReport, codebuild.CreateReportGroup, codebuild.CreateWebhook, codebuild.DeleteOAuthToken, codebuild.DeleteProject, codebuild.DeleteReport, codebuild.DeleteReportGroup, codebuild.DeleteResourcePolicy, codebuild.DeleteSourceCredentials, codebuild.DeleteWebhook, codebuild.DescribeTestCases, codebuild.GetResourcePolicy, codebuild.ImportSourceCredentials, codebuild.InvalidateProjectCache, codebuild.ListBuilds, codebuild.ListBuildsForProject, codebuild.ListConnectedOAuthAccounts, codebuild.ListCuratedEnvironmentImages, codebuild.ListProjects, codebuild.ListReportGroups, codebuild.ListReports, codebuild.ListReportsForReportGroup, codebuild.ListRepositories, codebuild.ListSharedProjects, codebuild.ListSharedReportGroups, codebuild.ListSourceCredentials, codebuild.PersistOAuthToken, codebuild.PutResourcePolicy, codebuild.StartBuild, codebuild.StopBuild, codebuild.UpdateProject, codebuild.UpdateReport, codebuild.UpdateReportGroup, codebuild.UpdateWebhook, iam.PassRole, codecommit.GitPull ], Resource=['*']), Statement(Effect=Allow, Principal=Principal("AWS", [IAM_ARN(user, '', account)]), Action=[ logs.FilterLogEventlogs, logs.GetLogEvents, logs.CreateLogGroup, logs.CreateLogStream, logs.PutLogEvents ], Resource=['*']), Statement(Effect=Allow, Principal=Principal("AWS", [IAM_ARN(user, '', account)]), Action=[ s3.CreateBucket, s3.ListAccessPoints, s3.ListAllMyBuckets, s3.ListBucket, s3.ListBucketByTags, s3.ListBucketMultipartUploads, s3.ListBucketVersions, s3.ListJobs, s3.ListMultipartUploadParts, s3.ListObjects, s3.PutObject, s3.GetObject, s3.GetBucketAcl, s3.GetBucketLocation, s3.GetObjectVersion ], Resource=['*']), Statement(Effect=Allow, Principal=Principal("AWS", [IAM_ARN(user, '', account)]), Action=[ ecr.BatchCheckLayerAvailability, ecr.GetDownloadUrlForLayer, ecr.BatchGetImage, ecr.PutImage, ecr.InitiateLayerUpload, ecr.UploadLayerPart, ecr.CompleteLayerUpload, ecr.GetAuthorizationToken ], Resource=['*']), Statement(Effect=Allow, Principal=Principal("AWS", [IAM_ARN(user, '', account)]), Action=[ec2.DescribeSecurityGroups, ec2.DescribeSubnets], Resource=['*']), Statement(Effect=Allow, Principal=Principal("AWS", [IAM_ARN(user, '', account)]), Action=[ ecs.RegisterTaskDefinition, ecs.DescribeTaskDefinition, ecs.DescribeServices, ecs.CreateService, ecs.ListServices, ecs.UpdateService ], Resource=['*']), ]) input_kwargs["policy"] = pd.to_json() return input_kwargs
CidrIp="0.0.0.0/0") ])) ud = Base64( Join('\n', [ "#!/bin/bash", "sudo yum install --enablerepo=epel -y git", "sudo yum install --enablerepo=epel -y ansible", AnsiblePullCmd, "echo '*/10 * * * * {}' > /etc/cron.d/ansible-pull".format( AnsiblePullCmd) ])) t.add_resource( Role("Role", AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[AssumeRole], Principal=Principal("Service", ["ec2.amazonaws.com"])) ]))) t.add_resource( InstanceProfile("InstanceProfile", Path="/", Roles=[Ref("Role")])) t.add_resource( IAMPolicy( "Policy", PolicyName="AllowS3", PolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[Action("s3", "*")], Resource=["*"]) ]))) t.add_resource(
# put it in just one. efs_mount_target = MountTarget("MyEFSMountTarget", FileSystemId=Ref(efs_file_system), SecurityGroups=[Ref(efs_security_group)], SubnetId=Ref(subnetid_param)) template.add_resource(efs_mount_target) # Create the policy that allows the instance to describe file systems and tags, # so it can lookup the file system using AWS tags. An alternative would be to # pass in the FileSystem name as UserData. efs_host_role = Role( "EFSHostRole", AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[ Action('elasticfilesystem', 'DescribeFileSystems'), Action('elasticfilesystem', 'DescribeTags') ], Resource=["*"]) ])) template.add_resource(efs_host_role) efs_host_instance_profile = InstanceProfile("EFSInstanceProfile", Roles=[Ref(efs_host_role)]) template.add_resource(efs_host_instance_profile) # And finally the EC2 instance. ec2_instance = Instance("Ec2Instance", ImageId=FindInMap("RegionMap", Ref("AWS::Region"), "AMI"), InstanceType="t1.micro", KeyName=Ref(keyname_param),
from troposphere.iam import Policy as IAMPolicy from troposphere.s3 import Bucket, VersioningConfiguration t = Template() t.set_description("myeongjae.kim: myeongjae.kim Pipeline") t.add_resource( Bucket("S3Bucket", VersioningConfiguration=VersioningConfiguration( Status="Enabled", ))) t.add_resource( Role("PipelineRole", AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[AssumeRole], Principal=Principal("Service", ["codepipeline.amazonaws.com"])) ]), Path="/", Policies=[ IAMPolicy(PolicyName="MyeongjaeKimCodePipeline", PolicyDocument={ "Statement": [ { "Effect": "Allow", "Action": "cloudformation:*", "Resource": "*" }, { "Effect": "Allow", "Action": "codebuild:*",
'EC2Principal': 'ec2.amazonaws.com', 'OpsWorksPrincipal': 'opsworks.amazonaws.com' }, 'us-west-2': { 'EC2Principal': 'ec2.amazonaws.com', 'OpsWorksPrincipal': 'opsworks.amazonaws.com' } }) web_server_role = Role( "WebServerRole", template=template, AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[AssumeRole], Principal=Principal("Service", [ FindInMap("Region2Principal", Ref("AWS::Region"), "EC2Principal") ])) ]), Path="/", Policies=[ assets_management_policy, logging_policy, iam.Policy( PolicyName="EBBucketAccess", PolicyDocument=dict(Statement=[ dict( Effect="Allow", Action=[ "s3:Get*", "s3:List*",
def CF_config(args): t = Template() t.add_description( "This AWS Cloudformation Template creates a stack necessary to run the SABER airflow pipeline." ) #Set up iamRole iamRole = t.add_resource( Role("ecsTaskWithS3", AssumeRolePolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement(Effect=Allow, Action=[AssumeRole], Principal=Principal( "Service", ["ecs-tasks.amazonaws.com"])) ]))) Vpc = t.add_parameter( Parameter( 'VpcId', ConstraintDescription= 'Must be a valid VPC ID. (Can be found here: https://console.aws.amazon.com/vpc/home?region=us-east-1#vpcs:sort=VpcId)', Type='String')) PrivateSubnetA = t.add_parameter( Parameter( 'SubnetId', ConstraintDescription= "Must be a valid subnet Id wihthin same VPC id. (Can be found here: https://console.aws.amazon.com/vpc/home?region=us-east-1#subnets:search=vpc-6443921c;sort=State)", Type='String')) keyname_param = t.add_parameter( Parameter("KeyName", Description="Key pair name for EC2 managment", Type="String")) sec_group = t.add_parameter( Parameter("SecurityGroup", Description="Security Group to access Ec2 instances", Type="String")) BatchServiceRole = t.add_resource( Role( 'BatchServiceRole', Path='/', Policies=[], ManagedPolicyArns=[ 'arn:aws:iam::aws:policy/AmazonS3FullAccess', 'arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole', ], AssumeRolePolicyDocument={ 'Statement': [{ 'Action': ['sts:AssumeRole'], 'Effect': 'Allow', 'Principal': { 'Service': ['batch.amazonaws.com'] } }] }, )) BatchInstanceRole = t.add_resource( Role( 'BatchInstanceRole', Path='/', Policies=[], ManagedPolicyArns=[ 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role' ], AssumeRolePolicyDocument={ 'Statement': [{ 'Action': ['sts:AssumeRole'], 'Effect': 'Allow', 'Principal': { 'Service': ['ec2.amazonaws.com'] } }] }, )) BatchInstanceProfile = t.add_resource( InstanceProfile( 'BatchInstanceProfile', Path="/", Roles=[Ref(BatchInstanceRole)], )) BatchSecurityGroup = t.add_resource( SecurityGroup( 'BatchSecurityGroup', VpcId=Ref(Vpc), #Default Vpc GroupDescription='Enable access to Batch instances', Tags=Tags(Name='batch-sg'), SecurityGroupIngress=[ # SecurityGroupRule( # IpProtocol="tcp", # FromPort="22", # ToPort="22", # CidrIp=Ref(sshlocation_param), # ), ])) BatchComputeEnvironment = t.add_resource( ComputeEnvironment( 'GeneralComputeEnvironment', Type='MANAGED', State='ENABLED', ServiceRole=Ref(BatchServiceRole), ComputeResources=ComputeResources( 'GeneralComputeResources', Type='EC2', DesiredvCpus=0, MinvCpus=0, MaxvCpus=256, InstanceTypes=['optimal'], InstanceRole=Ref(BatchInstanceProfile), SecurityGroupIds=[ GetAtt(BatchSecurityGroup, 'GroupId'), Ref(sec_group) ], Subnets=[Ref(PrivateSubnetA)], Ec2KeyPair=Ref(keyname_param)), ComputeEnvironmentName="saber-batch-compute-environment")) GPUBatchComputeEnvironment = t.add_resource( ComputeEnvironment( 'GPUComputeEnvironment', Type='MANAGED', State='ENABLED', ServiceRole=Ref(BatchServiceRole), ComputeResources=ComputeResources( 'GPUComputeResources', Type='EC2', DesiredvCpus=0, MinvCpus=0, MaxvCpus=512, ImageId="ami-0612e39997371a677", InstanceTypes=['p2.xlarge'], InstanceRole=Ref(BatchInstanceProfile), SecurityGroupIds=[ GetAtt(BatchSecurityGroup, 'GroupId'), Ref(sec_group) ], Subnets=[Ref(PrivateSubnetA)], Ec2KeyPair=Ref(keyname_param)), ComputeEnvironmentName="saber-batch-compute-environment-GPU-enabled" )) GPUJobQueue = t.add_resource( JobQueue('GPUJobQueue', ComputeEnvironmentOrder=[ ComputeEnvironmentOrder( ComputeEnvironment=Ref(GPUBatchComputeEnvironment), Order=1), ], Priority=1, State='ENABLED', JobQueueName=args.job_queue_gpu)) GenJobQueue = t.add_resource( JobQueue('GenJobQueue', ComputeEnvironmentOrder=[ ComputeEnvironmentOrder( ComputeEnvironment=Ref(BatchComputeEnvironment), Order=1), ], Priority=1, State='ENABLED', JobQueueName=args.job_queue_gen)) t.add_output([ Output('BatchComputeEnvironment', Value=Ref(BatchComputeEnvironment)), Output('BatchSecurityGroup', Value=Ref(BatchSecurityGroup)), Output('ExampleJobQueue', Value=Ref(GenJobQueue)), Output('GPUComputeEnvironment', Value=Ref(BatchComputeEnvironment)), Output('ExampleJobQueueGPU', Value=Ref(GPUJobQueue)) ]) # Finally, write the template to a file template_fp = os.path.dirname(sys.argv[0]) with open(template_fp + '/' + args.template_file, 'w') as f: f.write(t.to_json())
def __init__(self, paco_ctx, account_ctx, aws_region, stack_group, stack_tags, env_ctx, app_id, grp_id, cdapp, role): super().__init__(paco_ctx, account_ctx, aws_region, enabled=cdapp.is_enabled(), config_ref=cdapp.paco_ref_parts, iam_capabilities=["CAPABILITY_NAMED_IAM"], stack_group=stack_group, stack_tags=stack_tags) self.env_ctx = env_ctx self.set_aws_name('CodeDeployApplication', grp_id, cdapp.name) self.init_template('CodeDeploy Application') self.res_name_prefix = self.create_resource_name_join(name_list=[ self.env_ctx.get_aws_name(), app_id, grp_id, cdapp.name ], separator='-', camel_case=True) # Service Role ARN parameter service_role_arn_param = self.create_cfn_parameter( param_type='String', name='ServiceRoleArn', description='The codedeploy service Role to assume.', value=role.get_arn(), ) # CodeDeploy Application cdapp_resource = troposphere.codedeploy.Application( 'CodeDeployApplication', ComputePlatform=cdapp.compute_platform) self.template.add_resource(cdapp_resource) # DeploymentGroup resources for deploy_group in cdapp.deployment_groups.values(): if not deploy_group.is_enabled(): continue # Deployment configuration deploy_group_logical_id = self.create_cfn_logical_id( 'DeploymentGroup' + deploy_group.name) deployment_dict = { 'Description': deploy_group.title_or_name, } if deploy_group.ignore_application_stop_failures: deployment_dict[ 'IgnoreApplicationStopFailures'] = deploy_group.ignore_application_stop_failures if deploy_group.revision_location_s3: s3bucket = get_model_obj_from_ref( deploy_group.revision_location_s3.bucket, self.paco_ctx.project) deployment_dict['Revision'] = { 'S3Location': { 'Bucket': s3bucket.get_aws_name(), 'Key': deploy_group.revision_location_s3.key, }, 'RevisionType': 'S3' } if deploy_group.revision_location_s3.bundle_type: deployment_dict['Revision']['S3Location'][ 'BundleType'] = deploy_group.revision_location_s3.bundle_type cfn_export_dict = { 'Deployment': deployment_dict, 'ApplicationName': troposphere.Ref(cdapp_resource), 'ServiceRoleArn': troposphere.Ref(service_role_arn_param), } if deploy_group.autoscalinggroups: cfn_export_dict['AutoScalingGroups'] = [] for asg_ref in deploy_group.autoscalinggroups: asg = get_model_obj_from_ref(asg_ref, self.paco_ctx.project) cfn_export_dict['AutoScalingGroups'].append( asg.get_aws_name()) deploy_group_resource = troposphere.codedeploy.DeploymentGroup.from_dict( deploy_group_logical_id, cfn_export_dict) self.template.add_resource(deploy_group_resource) deploy_group_resource.DependsOn = [] deploy_group_resource.DependsOn.append(cdapp_resource.title) # User-defined Policies for policy in deploy_group.role_policies: policy_name = self.create_resource_name_join( name_list=[ self.res_name_prefix, 'CodeDeploy', deploy_group.name, 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)) policy_resource = troposphere.iam.PolicyType( title=self.create_cfn_logical_id('CodeDeployPolicy' + policy.name, camel_case=True), PolicyName=policy_name, PolicyDocument=PolicyDocument(Statement=statement_list, ), Roles=[troposphere.Ref(service_role_resource)]) self.template.add_resource(policy_resource) deploy_group_resource.DependsOn = policy_resource # All done, let's go home! self.set_template()
Parameter( 'Regions', Description='Region to search for SecurityGroups, separated by ","', Default='us-east-1', Type='String')) # # Resources # update_function_execution_role = t.add_resource( iam.Role( 'LambdaExecutionRole', AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[awacs.sts.AssumeRole], Principal=Principal('Service', ['lambda.amazonaws.com'])) ]), ManagedPolicyArns=[ 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', ], Policies=[ iam.Policy(PolicyName='AllowModifySecurityGroup', PolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement( Effect=Allow, Action=[ awacs.ec2.DescribeSecurityGroups, awacs.ec2.AuthorizeSecurityGroupIngress,
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)
def script_manager_ecs(self, ecs_group, asg_dict, asg_config, template): idx=0 policy_statements = [] for ecs_name in ecs_group.keys(): ecs = ecs_group[ecs_name] if ecs == None: continue ecs_script_manager_cluster_arn_param = self.create_cfn_parameter( param_type='String', name=f'ECSScriptManagerClusterArn{idx}', description=f'ECS Script Manager Cluster Arn {idx}', value=ecs.cluster + '.arn' ) ecs_cluster_tag = troposphere.autoscaling.Tag( f'paco:script_manager:ecs:{ecs_name}:cluster:arn', troposphere.Ref(ecs_script_manager_cluster_arn_param), True ) asg_dict['Tags'].append(ecs_cluster_tag) policy_statements.append( Statement( Sid=f'ECSScriptManagerClusterAccess{idx}', Effect=Allow, Action=[ Action('ecs', 'UpdateService'), Action('ecs', 'DescribeServices'), Action('ecs', 'ListServices'), Action('ecs', 'RunTask'), Action('ecs', 'StopTask'), Action('ecs', 'DescribeContainerInstances'), Action('ecs', 'ListTasks'), Action('ecs', 'DescribeTasks'), Action('ec2', 'DescribeInstances'), ], Resource=[ '*' ], Condition=Condition( StringEquals({ 'ecs:cluster': troposphere.Ref(ecs_script_manager_cluster_arn_param) }) ) ) ) policy_statements.append( Statement( Sid=f'ECSScriptManagerClusterEC2Access{idx}', Effect=Allow, Action=[ Action('ec2', 'DescribeInstances'), ], Resource=[ '*' ] ) ) idx += 1 script_manager_ecs_policy_res = troposphere.iam.ManagedPolicy( title='ScriptManagerECS', PolicyDocument=PolicyDocument( Version="2012-10-17", Statement=policy_statements ), Roles=[self.instance_iam_role_name] ) template.add_resource(script_manager_ecs_policy_res)
def __init__(self, stack, paco_ctx): super().__init__( stack, paco_ctx, iam_capabilities=["CAPABILITY_NAMED_IAM"], ) eventsrule = stack.resource config_ref = eventsrule.paco_ref_parts self.set_aws_name('EventsRule', self.resource_group_name, self.resource_name) self.notification_groups = {} # Init a Troposphere template self.init_template('CloudWatch EventsRule') if eventsrule.is_enabled() == False: return # Parameters schedule_expression_param = None if eventsrule.schedule_expression: schedule_expression_param = self.create_cfn_parameter( param_type='String', name='ScheduleExpression', description='ScheduleExpression for the Event Rule.', value=eventsrule.schedule_expression, ) description_param = self.create_cfn_parameter( param_type='String', name='EventDescription', description='Description for the Event Rule.', value=eventsrule.description, ) # Monitoring Target monitoring = self.resource.monitoring if monitoring != None and monitoring.is_enabled() == True: notifications = None if monitoring.notifications != None and len( monitoring.notifications.keys()) > 0: notifications = monitoring.notifications else: app_config = get_parent_by_interface(self.resource, schemas.IApplication) notifications = app_config.notifications if notifications != None and len(notifications.keys()) > 0: # Create the CF Param for the SNS ARN we need to Publish to notify_param_cache = [] for notify_group_name in notifications.keys(): for sns_group_name in notifications[ notify_group_name].groups: notify_param = self.create_notification_param( sns_group_name) # Only append if the are unique if notify_param not in notify_param_cache: eventsrule.targets.append(notify_param) notify_param_cache.append(notify_param) # Targets targets = [] self.target_params = {} target_invocation_role_resource = None for index in range(0, len(eventsrule.targets)): target = eventsrule.targets[index] # Target Parameters target_name = 'Target{}'.format(index) # Target CFN Parameters # Check if we already have a parameter object target_policy_actions = None if isinstance(target, troposphere.Parameter): self.target_params[target_name + 'Arn'] = target else: self.target_params[target_name + 'Arn'] = self.create_cfn_parameter( param_type='String', name=target_name + 'Arn', description=target_name + ' Arn for the Events Rule.', value=target.target + '.arn', ) # If the target is a reference, get the target object from the model # to check what type of resource we need to configure for target_ref = Reference(target.target) if target_ref.parts[-1] == 'project' and target_ref.parts[ -3] == 'build': codebuild_target_ref = f'paco.ref {".".join(target_ref.parts[:-1])}' target_model_obj = get_model_obj_from_ref( codebuild_target_ref, self.paco_ctx.project) else: target_model_obj = get_model_obj_from_ref( target.target, self.paco_ctx.project) # Lambda Policy Actions if schemas.IDeploymentPipelineBuildCodeBuild.providedBy( target_model_obj): # CodeBuild Project target_policy_actions = [awacs.codebuild.StartBuild] elif schemas.ILambda.providedBy(target_model_obj): # Lambda Function target_policy_actions = [awacs.awslambda.InvokeFunction] self.target_params[target_name] = self.create_cfn_parameter( param_type='String', name=target_name, description=target_name + ' for the Event Rule.', value=target_name, ) # IAM Role Polcies by Resource type if target_policy_actions != None: # IAM Role Resources to allow Event to invoke Target target_invocation_role_resource = troposphere.iam.Role( 'TargetInvocationRole', AssumeRolePolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement(Effect=Allow, Action=[awacs.sts.AssumeRole], Principal=Principal( 'Service', ['events.amazonaws.com'])) ], ), Policies=[ troposphere.iam.Policy( PolicyName="TargetInvocation", PolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement( Effect=Allow, Action=target_policy_actions, Resource=[ troposphere.Ref( self.target_params[target_name + 'Arn']) ], ) ])) ], ) self.template.add_resource(target_invocation_role_resource) # Create Target CFN Resources cfn_export_dict = { 'Arn': troposphere.Ref(self.target_params[target_name + 'Arn']), 'Id': troposphere.Ref(self.target_params[target_name]) } if target_invocation_role_resource != None: cfn_export_dict['RoleArn'] = troposphere.GetAtt( target_invocation_role_resource, 'Arn') if hasattr(target, 'input_json') and target.input_json != None: cfn_export_dict['Input'] = target.input_json # Events Rule Targets targets.append(cfn_export_dict) # Events Rule Resource # The Name is needed so that a Lambda can be created and it's Lambda ARN output # can be supplied as a Parameter to this Stack and a Lambda Permission can be # made with the Lambda. Avoids circular dependencies. name = create_event_rule_name(eventsrule) if eventsrule.enabled_state: enabled_state = 'ENABLED' else: enabled_state = 'DISABLED' events_rule_dict = { 'Name': name, 'Description': troposphere.Ref(description_param), 'Targets': targets, 'State': enabled_state } if target_invocation_role_resource != None: events_rule_dict['RoleArn'] = troposphere.GetAtt( target_invocation_role_resource, 'Arn') if schedule_expression_param != None: events_rule_dict['ScheduleExpression'] = troposphere.Ref( schedule_expression_param) elif eventsrule.event_pattern != None: source_value_list = [] project_name_list = [] for pattern_source in eventsrule.event_pattern.source: if is_ref(pattern_source): source_obj = get_model_obj_from_ref( pattern_source, self.paco_ctx.project) if schemas.IDeploymentPipelineBuildCodeBuild.providedBy( source_obj): source_value_list.append('aws.codebuild') project_name_list.append( source_obj._stack.template.get_project_name()) else: raise InvalidEventsRuleEventPatternSource( pattern_source) else: source_value_list.append(pattern_source) if len(project_name_list) > 0: eventsrule.event_pattern.detail[ 'project-name'] = project_name_list event_pattern_dict = { 'source': source_value_list, 'detail-type': utils.obj_to_dict(eventsrule.event_pattern.detail_type), 'detail': utils.obj_to_dict(eventsrule.event_pattern.detail), } event_pattern_yaml = yaml.dump(event_pattern_dict) events_rule_dict['EventPattern'] = yaml.load(event_pattern_yaml) else: # Defaults to a CodePipeline events rule event_pattern_yaml = """ source: - aws.codepipeline detail-type: - 'CodePipeline Pipeline Execution State Change' detail: state: - STARTED """ events_rule_dict['EventPattern'] = yaml.load(event_pattern_yaml) event_rule_resource = troposphere.events.Rule.from_dict( 'EventRule', events_rule_dict) if target_invocation_role_resource != None: event_rule_resource.DependsOn = target_invocation_role_resource self.template.add_resource(event_rule_resource) # Outputs self.create_output( title="EventRuleId", value=troposphere.Ref(event_rule_resource), ref=config_ref + '.id', ) self.create_output( title="EventRuleArn", value=troposphere.GetAtt(event_rule_resource, "Arn"), ref=config_ref + '.arn', )
def kms_key_policy(key_use_arns, key_admin_arns): """ Creates a key policy for use of a KMS Key. key_use_arns is a list of arns that should have access to use the KMS key. """ root_arn = Join(":", ["arn:aws:iam:", Ref("AWS::AccountId"), "root"]) statements = [] statements.append( Statement(Sid="Enable IAM User Permissions", Effect=Allow, Principal=AWSPrincipal(root_arn), Action=[ Action("kms", "*"), ], Resource=["*"])) statements.append( Statement(Sid="Allow use of the key", Effect=Allow, Principal=AWSPrincipal(key_use_arns), Action=[ awacs.kms.Encrypt, awacs.kms.Decrypt, awacs.kms.ReEncrypt, awacs.kms.GenerateDataKey, awacs.kms.GenerateDataKeyWithoutPlaintext, awacs.kms.DescribeKey, ], Resource=["*"])) statements.append( Statement(Sid="Allow attachment of persistent resources", Effect=Allow, Principal=AWSPrincipal(key_use_arns), Action=[ awacs.kms.CreateGrant, awacs.kms.ListGrants, awacs.kms.RevokeGrant, ], Resource=["*"], Condition=Condition(Bool("kms:GrantIsForAWSResource", True)))) statements.append( Statement( Sid="Allow access for Key Administrators", Effect=Allow, Principal=AWSPrincipal(key_admin_arns), Action=[ Action("kms", "Create*"), Action("kms", "Describe*"), Action("kms", "Enable*"), Action("kms", "List*"), Action("kms", "Put*"), Action("kms", "Update*"), Action("kms", "Revoke*"), Action("kms", "Disable*"), Action("kms", "Get*"), Action("kms", "Delete*"), Action("kms", "ScheduleKeyDeletion"), Action("kms", "CancelKeyDeletion"), ], Resource=["*"], )) return Policy(Version="2012-10-17", Id="key-default-1", Statement=statements)
"EC2Principal": "ec2.amazonaws.com", "OpsWorksPrincipal": "opsworks.amazonaws.com", }, }, ) t.add_resource( Role( "WebServerRole", AssumeRolePolicyDocument=PolicyDocument(Statement=[ Statement( Effect=Allow, Action=[AssumeRole], Principal=Principal( "Service", [ FindInMap("Region2Principal", Ref("AWS::Region"), "EC2Principal") ], ), ) ]), Path="/", )) t.add_resource( IAMPolicy( "WebServerRolePolicy", PolicyName="WebServerRole", PolicyDocument=PolicyDocument(Statement=[ Statement(
def create_template(self): """Create template (main function called by Stacker).""" template = self.template variables = self.get_variables() self.template.add_version('2010-09-09') self.template.add_description('Terraform State Resources') # Conditions for i in ['BucketName', 'TableName']: template.add_condition( "%sOmitted" % i, Or(Equals(variables[i].ref, ''), Equals(variables[i].ref, 'undefined'))) # Resources terraformlocktable = template.add_resource( dynamodb.Table( 'TerraformStateTable', AttributeDefinitions=[ dynamodb.AttributeDefinition(AttributeName='LockID', AttributeType='S') ], KeySchema=[ dynamodb.KeySchema(AttributeName='LockID', KeyType='HASH') ], ProvisionedThroughput=dynamodb.ProvisionedThroughput( ReadCapacityUnits=2, WriteCapacityUnits=2), TableName=If('TableNameOmitted', NoValue, variables['TableName'].ref))) template.add_output( Output('%sName' % terraformlocktable.title, Description='Name of DynamoDB table for Terraform state', Value=terraformlocktable.ref())) terraformstatebucket = template.add_resource( s3.Bucket( 'TerraformStateBucket', AccessControl=s3.Private, BucketName=If('BucketNameOmitted', NoValue, variables['BucketName'].ref), LifecycleConfiguration=s3.LifecycleConfiguration(Rules=[ s3.LifecycleRule(NoncurrentVersionExpirationInDays=90, Status='Enabled') ]), VersioningConfiguration=s3.VersioningConfiguration( Status='Enabled'))) template.add_output( Output('%sName' % terraformstatebucket.title, Description='Name of bucket storing Terraform state', Value=terraformstatebucket.ref())) template.add_output( Output('%sArn' % terraformstatebucket.title, Description='Arn of bucket storing Terraform state', Value=terraformstatebucket.get_att('Arn'))) managementpolicy = template.add_resource( iam.ManagedPolicy( 'ManagementPolicy', Description='Managed policy for Terraform state management.', Path='/', PolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ # https://www.terraform.io/docs/backends/types/s3.html#s3-bucket-permissions Statement( Action=[awacs.s3.ListBucket], Effect=Allow, Resource=[terraformstatebucket.get_att('Arn')]), Statement( Action=[awacs.s3.GetObject, awacs.s3.PutObject], Effect=Allow, Resource=[ Join('', [ terraformstatebucket.get_att('Arn'), '/*' ]) ]), Statement(Action=[ awacs.dynamodb.GetItem, awacs.dynamodb.PutItem, awacs.dynamodb.DeleteItem ], Effect=Allow, Resource=[terraformlocktable.get_att('Arn')]) ]))) template.add_output( Output('PolicyArn', Description='Managed policy Arn', Value=managementpolicy.ref()))
def __init__(self, stack, paco_ctx,): rds_config = stack.resource config_ref = rds_config.paco_ref_parts super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_IAM"]) self.set_aws_name('RDS', self.resource_group_name, self.resource.name) self.init_template('RDS') template = self.template if not rds_config.is_enabled(): return rds_logical_id = 'PrimaryDBInstance' # DB Subnet Group db_subnet_id_list_param = self.create_cfn_parameter( param_type='List<AWS::EC2::Subnet::Id>', name='DBSubnetIdList', description='The list of subnet IDs where this database will be provisioned.', value=rds_config.segment+'.subnet_id_list', ) db_subnet_group_res = troposphere.rds.DBSubnetGroup( title='DBSubnetGroup', template =template, DBSubnetGroupDescription=troposphere.Ref('AWS::StackName'), SubnetIds=troposphere.Ref(db_subnet_id_list_param), ) # DB Parameter Group engine_major_version = None if rds_config.parameter_group == None: # No Parameter Group supplied, create one engine_major_version = '.'.join(rds_config.engine_version.split('.')[0:2]) param_group_family = gen_vocabulary.rds_engine_versions[rds_config.engine][rds_config.engine_version]['param_group_family'] dbparametergroup_ref = troposphere.rds.DBParameterGroup( "DBParameterGroup", template = template, Family=param_group_family, Description=troposphere.Ref('AWS::StackName') ) else: # Use an existing Parameter Group dbparametergroup_ref = self.create_cfn_parameter( name='DBParameterGroupName', param_type='String', description='DB Parameter Group Name', value=rds_config.parameter_group + '.name', ) # Option Group option_group_res = None if len(rds_config.option_configurations) > 0 or (hasattr(rds_config, 'backup_restore_bucket') and rds_config.backup_restore_bucket != None): option_group_dict = { 'EngineName': rds_config.engine, 'MajorEngineVersion': engine_major_version, 'OptionGroupDescription': troposphere.Ref('AWS::StackName') } option_config_list = [] if len(rds_config.option_configurations) > 0: for option_config in rds_config.option_configurations: option_config_dict = { 'OptionName': option_config.option_name, } if len(option_config.option_settings) > 0: option_config_dict['OptionSettings'] = [] idx = 0 for option_setting in option_config.option_settings: option_value = option_setting.value if references.is_ref(option_setting.value): # Use an existing Parameter Group option_setting_value_param = self.create_cfn_parameter( name=f'OptionsGroupValue{idx}', param_type='String', description=f'DB Option Settings Value {idx}', value=option_setting.value ) option_value = troposphere.Ref(option_setting_value_param) option_setting_dict = { 'Name': option_setting.name, 'Value': option_value } option_config_dict['OptionSettings'].append(option_setting_dict) option_config_list.append(option_config_dict) if hasattr(rds_config, 'backup_restore_bucket') and rds_config.backup_restore_bucket != None: option_config_dict = { 'OptionName': 'SQLSERVER_BACKUP_RESTORE', 'OptionSettings': [] } # S3 Bucket Arn Param backup_restore_bucket_arn_param = self.create_cfn_parameter( name='SQLServerBackupRestoreBucketArn', param_type='String', description=f'DB Option Setting SQLServer Backup Restore Bucket ARN', value=f'{rds_config.backup_restore_bucket}.arn' ) # Create Role for SQLServer Bucket sqlserver_backup_restore_role = troposphere.iam.Role( title='SQLServerBackupRestoreRole', template=self.template, AssumeRolePolicyDocument=PolicyDocument( Statement=[ Statement( Effect=Allow, Action=[Action("sts", "AssumeRole")], Principal=Principal("Service", "rds.amazonaws.com") ) ] ), Policies=[ troposphere.iam.Policy( PolicyName="S3BucketAccess", PolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement( Effect=Allow, Action=[ awacs.s3.ListBucket, awacs.s3.GetBucketLocation ], Resource=[troposphere.Ref(backup_restore_bucket_arn_param)], ), Statement( Effect=Allow, Action=[ Action('s3', 'GetObjectMetaData'), awacs.s3.GetObject, awacs.s3.PutObject, awacs.s3.ListMultipartUploadParts, awacs.s3.AbortMultipartUpload ], Resource=[troposphere.Sub("${SQLServerBackupRestoreBucketArn}/*")] ) ] ) ) ], Path="/", ) option_value = troposphere.GetAtt(sqlserver_backup_restore_role, 'Arn') option_setting_dict = { 'Name': 'IAM_ROLE_ARN', 'Value': option_value } option_config_dict['OptionSettings'].append(option_setting_dict) option_config_list.append(option_config_dict) option_group_dict['OptionConfigurations'] = option_config_list option_group_res = troposphere.rds.OptionGroup.from_dict( 'OptionGroup', option_group_dict ) template.add_resource(option_group_res) # RDS MultiAZ (Mysql, Postgresql) sg_param_ref_list = [] for sg_ref in rds_config.security_groups: sg_hash = utils.md5sum(str_data=sg_ref) sg_param = self.create_cfn_parameter( param_type='AWS::EC2::SecurityGroup::Id', name=self.create_cfn_logical_id('SecurityGroup'+sg_hash), description='VPC Security Group to attach to the RDS.', value=sg_ref+'.id', ) sg_param_ref_list.append(troposphere.Ref(sg_param)) db_instance_dict = { 'Engine': rds_config.engine, 'EngineVersion': rds_config.engine_version, 'DBInstanceIdentifier': troposphere.Ref('AWS::StackName'), 'DBInstanceClass': rds_config.db_instance_type, 'DBSubnetGroupName': troposphere.Ref(db_subnet_group_res), 'DBParameterGroupName': troposphere.Ref(dbparametergroup_ref), 'CopyTagsToSnapshot': True, 'AllowMajorVersionUpgrade': rds_config.allow_major_version_upgrade, 'AutoMinorVersionUpgrade': rds_config.auto_minor_version_upgrade, 'AllocatedStorage': rds_config.storage_size_gb, 'StorageType': rds_config.storage_type, 'BackupRetentionPeriod': rds_config.backup_retention_period, 'Port': rds_config.port, 'PreferredBackupWindow': rds_config.backup_preferred_window, 'PreferredMaintenanceWindow': rds_config.maintenance_preferred_window, 'VPCSecurityGroups': sg_param_ref_list } if schemas.IRDSMultiAZ.providedBy(rds_config): db_instance_dict['MultiAZ'] = rds_config.multi_az # License Model if rds_config.license_model: db_instance_dict['LicenseModel'] = rds_config.license_model # Deletion Protection if rds_config.deletion_protection: db_instance_dict['DeletionProtection'] = rds_config.deletion_protection # CloudWatch Logs Exports if len(rds_config.cloudwatch_logs_exports) > 0: db_instance_dict['EnableCloudwatchLogsExports'] = rds_config.cloudwatch_logs_exports # Option Group if option_group_res != None: db_instance_dict['OptionGroupName'] = troposphere.Ref(option_group_res) # DB Snapshot Identifier if rds_config.db_snapshot_identifier == '' or rds_config.db_snapshot_identifier == None: db_snapshot_id_enabled = False else: db_snapshot_id_enabled = True if db_snapshot_id_enabled == True: db_instance_dict['DBSnapshotIdentifier'] = rds_config.db_snapshot_identifier # To restore an existing DB from a Snapshot, RDS will need to replace the RDS # resource, in which case the DBInstanceIdentifier name CAN NOT be set # del db_instance_dict['DBInstanceIdentifier'] # Username and Passsword if db_snapshot_id_enabled == False: # Encryption if rds_config.storage_encrypted == True: db_instance_dict['StorageEncrypted'] = True if rds_config.kms_key_id and rds_config.kms_key_id != '': db_instance_dict['KmsKeyId'] = rds_config.kms_key_id # Username & Password db_instance_dict['MasterUsername'] = rds_config.master_username if rds_config.secrets_password: # Password from Secrets Manager sta_logical_id = 'SecretTargetAttachmentRDS' secret_arn_param = self.create_cfn_parameter( param_type='String', name='RDSSecretARN', description='The ARN for the secret for the RDS master password.', value=rds_config.secrets_password + '.arn', ) secret_target_attachment_resource = troposphere.secretsmanager.SecretTargetAttachment( title=sta_logical_id, SecretId=troposphere.Ref(secret_arn_param), TargetId=troposphere.Ref(rds_logical_id), TargetType='AWS::RDS::DBInstance' ) template.add_resource(secret_target_attachment_resource) db_instance_dict['MasterUserPassword'] = troposphere.Join( '', ['{{resolve:secretsmanager:', troposphere.Ref(secret_arn_param), ':SecretString:password}}' ] ) else: master_password_param = self.create_cfn_parameter( param_type='String', name='MasterUserPassword', description='The master user password.', value=rds_config.master_user_password, noecho=True, ) db_instance_dict['MasterUserPassword'] = troposphere.Ref(master_password_param) db_instance_res = troposphere.rds.DBInstance.from_dict( rds_logical_id, db_instance_dict ) template.add_resource(db_instance_res) # Outputs self.create_output( title='DBInstanceName', description='DB Instance Name', value=troposphere.Ref(db_instance_res), ref=config_ref + ".name", ) self.create_output( title='RDSEndpointAddress', description='RDS Endpoint URL', value=troposphere.GetAtt(db_instance_res, 'Endpoint.Address'), ref=config_ref + ".endpoint.address", ) # Legacy Route53 Record Set if self.paco_ctx.legacy_flag('route53_record_set_2019_10_16') == True: if rds_config.is_dns_enabled() == True: for dns_config in rds_config.dns_config: dns_hash = utils.md5sum(str_data=(rds_config.hosted_zone+rds_config.domain_name)) primary_hosted_zone_id_param = self.create_cfn_parameter( param_type='String', name='DNSHostedZoneId'+dns_hash, description='The hosted zone id to create the Route53 record set.', value=rds_config.primary_hosted_zone+'.id', ) record_set_res = troposphere.route53.RecordSetType( title = 'RecordSet'+dns_hash, template = template, Comment = 'RDS Primary DNS', HostedZoneId = troposphere.Ref(primary_hosted_zone_id_param), Name = rds_config.primary_domain_name, Type = 'CNAME', TTL = dns_config.ttl, ResourceRecords = [ troposphere.GetAtt(db_instance_res, 'Endpoint.Address')] ) record_set_res.DependsOn = db_instance_res # DNS - Route53 Record Set if self.paco_ctx.legacy_flag('route53_record_set_2019_10_16') == False: if rds_config.is_dns_enabled() == True: route53_ctl = self.paco_ctx.get_controller('route53') for dns_config in rds_config.dns: route53_ctl.add_record_set( self.account_ctx, self.aws_region, rds_config, enabled=rds_config.is_enabled(), dns=dns_config, record_set_type='CNAME', resource_records=['paco.ref ' + config_ref + '.endpoint.address'], stack_group=self.stack.stack_group, async_stack_provision=True, config_ref=rds_config.paco_ref_parts + '.dns' )
def __init__( self, stack, paco_ctx, ): super().__init__( stack, paco_ctx, iam_capabilities=["CAPABILITY_NAMED_IAM"], ) account_ctx = stack.account_ctx aws_region = stack.aws_region self.set_aws_name('Lambda', self.resource_group_name, self.resource_name) awslambda = self.awslambda = self.stack.resource self.init_template('Lambda Function') # if not enabled finish with only empty placeholder if not awslambda.is_enabled(): return # Parameters sdb_cache_param = self.create_cfn_parameter( name='EnableSDBCache', param_type='String', description= 'Boolean indicating whether an SDB Domain will be created to be used as a cache.', value=awslambda.sdb_cache) function_description_param = self.create_cfn_parameter( name='FunctionDescription', param_type='String', description='A description of the Lamdba Function.', value=awslambda.description) handler_param = self.create_cfn_parameter( name='Handler', param_type='String', description='The name of the function to call upon execution.', value=awslambda.handler) runtime_param = self.create_cfn_parameter( name='Runtime', param_type='String', description='The name of the runtime language.', value=awslambda.runtime) role_arn_param = self.create_cfn_parameter( name='RoleArn', param_type='String', description='The execution role for the Lambda Function.', value=awslambda.iam_role.get_arn()) role_name_param = self.create_cfn_parameter( name='RoleName', param_type='String', description='The execution role name for the Lambda Function.', value=awslambda.iam_role.resolve_ref_obj.role_name) memory_size_param = self.create_cfn_parameter( name='MemorySize', param_type='Number', description="The amount of memory that your function has access to. Increasing the function's" + \ " memory also increases its CPU allocation. The default value is 128 MB. The value must be a multiple of 64 MB.", value=awslambda.memory_size ) reserved_conc_exec_param = self.create_cfn_parameter( name='ReservedConcurrentExecutions', param_type='Number', description= 'The number of simultaneous executions to reserve for the function.', value=awslambda.reserved_concurrent_executions) timeout_param = self.create_cfn_parameter( name='Timeout', param_type='Number', description= 'The amount of time that Lambda allows a function to run before stopping it. ', value=awslambda.timeout) layers_param = self.create_cfn_parameter( name='Layers', param_type='CommaDelimitedList', description='List of up to 5 Lambda Layer ARNs.', value=','.join(awslambda.layers)) # create the Lambda resource cfn_export_dict = { 'Description': troposphere.Ref(function_description_param), 'Handler': troposphere.Ref(handler_param), 'MemorySize': troposphere.Ref(memory_size_param), 'Runtime': troposphere.Ref(runtime_param), 'Role': troposphere.Ref(role_arn_param), 'Timeout': troposphere.Ref(timeout_param), } if awslambda.reserved_concurrent_executions: cfn_export_dict['ReservedConcurrentExecutions'] = troposphere.Ref( reserved_conc_exec_param), if len(awslambda.layers) > 0: cfn_export_dict['Layers'] = troposphere.Ref(layers_param), # Lambda VPC if awslambda.vpc_config != None: vpc_security_group = self.create_cfn_ref_list_param( name='VpcSecurityGroupIdList', param_type='List<AWS::EC2::SecurityGroup::Id>', description='VPC Security Group Id List', value=awslambda.vpc_config.security_groups, ref_attribute='id', ) # Segment SubnetList is a Segment stack Output based on availability zones segment_ref = awslambda.vpc_config.segments[0] + '.subnet_id_list' subnet_list_param = self.create_cfn_parameter( name='VpcSubnetIdList', param_type='List<AWS::EC2::Subnet::Id>', description='VPC Subnet Id List', value=segment_ref) cfn_export_dict['VpcConfig'] = { 'SecurityGroupIds': troposphere.Ref(vpc_security_group), 'SubnetIds': troposphere.Ref(subnet_list_param), } # Code object: S3 Bucket, inline ZipFile or deploy artifact? if awslambda.code.s3_bucket: if awslambda.code.s3_bucket.startswith('paco.ref '): value = awslambda.code.s3_bucket + ".name" else: value = awslambda.code.s3_bucket s3bucket_param = self.create_cfn_parameter( name='CodeS3Bucket', description= "An Amazon S3 bucket in the same AWS Region as your function. The bucket can be in a different AWS account.", param_type='String', value=value) s3key_param = self.create_cfn_parameter( name='CodeS3Key', description="The Amazon S3 key of the deployment package.", param_type='String', value=awslambda.code.s3_key) cfn_export_dict['Code'] = { 'S3Bucket': troposphere.Ref(s3bucket_param), 'S3Key': troposphere.Ref(s3key_param), } else: zip_path = Path(awslambda.code.zipfile) if zip_path.is_file(): cfn_export_dict['Code'] = {'ZipFile': zip_path.read_text()} elif zip_path.is_dir(): # get S3Bucket/S3Key or if it does not exist, it will create the bucket and artifact # and then upload the artifact bucket_name, artifact_name = init_lambda_code( self.paco_ctx.paco_buckets, self.stack.resource, awslambda.code.zipfile, self.stack.account_ctx, self.stack.aws_region, ) s3bucket_param = self.create_cfn_parameter( name='CodeS3Bucket', description="The Paco S3 Bucket for configuration", param_type='String', value=bucket_name) s3key_param = self.create_cfn_parameter( name='CodeS3Key', description="The Lambda code artifact S3 Key.", param_type='String', value=artifact_name) cfn_export_dict['Code'] = { 'S3Bucket': troposphere.Ref(s3bucket_param), 'S3Key': troposphere.Ref(s3key_param), } # Environment variables var_export = {} if awslambda.environment != None and awslambda.environment.variables != None: for var in awslambda.environment.variables: name = var.key.replace('_', '') env_param = self.create_cfn_parameter( name='EnvVar{}'.format(name), param_type='String', description='Env var for {}'.format(name), value=var.value, ) var_export[var.key] = troposphere.Ref(env_param) if awslambda.sdb_cache == True: var_export['SDB_CACHE_DOMAIN'] = troposphere.Ref( 'LambdaSDBCacheDomain') if len(awslambda.log_group_names) > 0: # Add PACO_LOG_GROUPS Environment Variable var_export['PACO_LOG_GROUPS'] = [ prefixed_name(awslambda, loggroup_name, self.paco_ctx.legacy_flag) for loggroup_name in awslambda.log_group_names ] cfn_export_dict['Environment'] = {'Variables': var_export} # Lambda resource self.awslambda_resource = troposphere.awslambda.Function.from_dict( 'Function', cfn_export_dict) self.template.add_resource(self.awslambda_resource) # SDB Cache with SDB Domain and SDB Domain Policy resources if awslambda.sdb_cache == True: sdb_domain_resource = troposphere.sdb.Domain( title='LambdaSDBCacheDomain', template=self.template, Description="Lambda Function Domain") sdb_policy = troposphere.iam.Policy( title='LambdaSDBCacheDomainPolicy', template=self.template, PolicyName='SDBDomain', PolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement( Effect=Allow, Action=[Action("sdb", "*")], Resource=[ troposphere.Sub( 'arn:aws:sdb:${AWS::Region}:${AWS::AccountId}:domain/${DomainName}', DomainName=troposphere.Ref( 'LambdaSDBCacheDomain')) ], ) ], Roles=troposphere.Ref(role_arn_param))) sdb_policy.DependsOn = sdb_domain_resource self.awslambda_resource.DependsOn = sdb_domain_resource # Permissions # SNS Topic Lambda permissions and subscription idx = 1 for sns_topic_ref in awslambda.sns_topics: # SNS Topic Arn parameters param_name = 'SNSTopicArn%d' % idx self.create_cfn_parameter( name=param_name, param_type='String', description='An SNS Topic ARN to grant permission to.', value=sns_topic_ref + '.arn') # Lambda permission troposphere.awslambda.Permission( title=param_name + 'Permission', template=self.template, Action="lambda:InvokeFunction", FunctionName=troposphere.GetAtt(self.awslambda_resource, 'Arn'), Principal='sns.amazonaws.com', SourceArn=troposphere.Ref(param_name), ) # SNS Topic subscription sns_topic = get_model_obj_from_ref(sns_topic_ref, self.paco_ctx.project) troposphere.sns.SubscriptionResource( title=param_name + 'Subscription', template=self.template, Endpoint=troposphere.GetAtt(self.awslambda_resource, 'Arn'), Protocol='lambda', TopicArn=troposphere.Ref(param_name), Region=sns_topic.region_name) idx += 1 # Lambda permissions for connected Paco resources app = get_parent_by_interface(awslambda, schemas.IApplication) for obj in get_all_nodes(app): # S3 Bucket notification permission(s) if schemas.IS3Bucket.providedBy(obj): seen = {} if hasattr(obj, 'notifications'): if hasattr(obj.notifications, 'lambdas'): for lambda_notif in obj.notifications.lambdas: if lambda_notif.function == awslambda.paco_ref: # yes, this Lambda gets notification from this S3Bucket group = get_parent_by_interface( obj, schemas.IResourceGroup) s3_logical_name = self.gen_cf_logical_name( group.name + obj.name, '_') if s3_logical_name not in seen: troposphere.awslambda.Permission( title='S3Bucket' + s3_logical_name, template=self.template, Action="lambda:InvokeFunction", FunctionName=troposphere.GetAtt( self.awslambda_resource, 'Arn'), Principal='s3.amazonaws.com', SourceArn='arn:aws:s3:::' + obj.get_bucket_name(), ) seen[s3_logical_name] = True # Events Rule permission(s) if schemas.IEventsRule.providedBy(obj): seen = {} for target in obj.targets: target_ref = Reference(target.target) target_ref.set_account_name(account_ctx.get_name()) target_ref.set_region(aws_region) lambda_ref = Reference(awslambda.paco_ref) if target_ref.raw == lambda_ref.raw: # yes, the Events Rule has a Target that is this Lambda group = get_parent_by_interface( obj, schemas.IResourceGroup) eventsrule_logical_name = self.gen_cf_logical_name( group.name + obj.name, '_') if eventsrule_logical_name not in seen: rule_name = create_event_rule_name(obj) # rule_name = self.create_cfn_logical_id("EventsRule" + obj.paco_ref) # rule_name = hash_smaller(rule_name, 64) source_arn = 'arn:aws:events:{}:{}:rule/{}'.format( aws_region, account_ctx.id, rule_name) troposphere.awslambda.Permission( title='EventsRule' + eventsrule_logical_name, template=self.template, Action="lambda:InvokeFunction", FunctionName=troposphere.GetAtt( self.awslambda_resource, 'Arn'), Principal='events.amazonaws.com', SourceArn=source_arn, ) seen[eventsrule_logical_name] = True # IoT Analytics permission(s) if schemas.IIoTAnalyticsPipeline.providedBy(obj): seen = {} for activity in obj.pipeline_activities.values(): if activity.activity_type == 'lambda': target_ref = Reference(activity.function) target_ref.set_account_name(account_ctx.get_name()) target_ref.set_region(aws_region) lambda_ref = Reference(awslambda.paco_ref) if target_ref.raw == lambda_ref.raw: # yes, the IoT Analytics Lambda Activity has a ref to this Lambda group = get_parent_by_interface( obj, schemas.IResourceGroup) iotap_logical_name = self.gen_cf_logical_name( group.name + obj.name, '_') if iotap_logical_name not in seen: rule_name = create_event_rule_name(obj) troposphere.awslambda.Permission( title='IoTAnalyticsPipeline' + iotap_logical_name, template=self.template, Action="lambda:InvokeFunction", FunctionName=troposphere.GetAtt( self.awslambda_resource, 'Arn'), Principal='iotanalytics.amazonaws.com', ) seen[iotap_logical_name] = True # Log group(s) loggroup_function_name = troposphere.Join('', [ '/aws/lambda/', troposphere.Select( 6, troposphere.Split( ':', troposphere.GetAtt(self.awslambda_resource, 'Arn'))) ]) loggroup_resources = [] loggroup_resources.append( self.add_log_group(loggroup_function_name, 'lambda')) if len(awslambda.log_group_names) > 0: # Additional App-specific LogGroups for loggroup_name in awslambda.log_group_names: # Add LogGroup to the template prefixed_loggroup_name = prefixed_name( awslambda, loggroup_name, self.paco_ctx.legacy_flag) loggroup_resources.append( self.add_log_group(prefixed_loggroup_name)) # LogGroup permissions log_group_arns = [ troposphere.Join(':', [ f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group', loggroup_function_name, '*' ]) ] log_stream_arns = [ troposphere.Join(':', [ f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group', loggroup_function_name, 'log-stream', '*' ]) ] for loggroup_name in awslambda.log_group_names: prefixed_loggroup_name = prefixed_name(awslambda, loggroup_name, self.paco_ctx.legacy_flag) log_group_arns.append( f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group:{prefixed_loggroup_name}:*' ) log_stream_arns.append( f'arn:aws:logs:{self.aws_region}:{account_ctx.id}:log-group:{prefixed_loggroup_name}:log-stream:*' ) loggroup_policy_resource = troposphere.iam.ManagedPolicy( title='LogGroupManagedPolicy', PolicyDocument=Policy( Version='2012-10-17', Statement=[ Statement( Sid='AllowLambdaModifyLogStreams', Effect=Allow, Action=[ Action("logs", "CreateLogStream"), Action("logs", "DescribeLogStreams"), ], Resource=log_group_arns, ), Statement( Sid='AllowLambdaPutLogEvents', Effect=Allow, Action=[ Action("logs", "PutLogEvents"), ], Resource=log_stream_arns, ), ], ), Roles=[troposphere.Ref(role_name_param)], ) loggroup_policy_resource.DependsOn = loggroup_resources self.template.add_resource(loggroup_policy_resource) # Outputs self.create_output( title='FunctionName', value=troposphere.Ref(self.awslambda_resource), ref=awslambda.paco_ref_parts + '.name', ) self.create_output( title='FunctionArn', value=troposphere.GetAtt(self.awslambda_resource, 'Arn'), ref=awslambda.paco_ref_parts + '.arn', )
def __init__(self, stack, paco_ctx,): rds_aurora = stack.resource super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_IAM"]) self.set_aws_name('RDSAurora', self.resource_group_name, self.resource.name) self.init_template('RDSAurora') if not rds_aurora.is_enabled(): return rds_cluster_logical_id = 'DBCluster' db_cluster_dict = rds_aurora.cfn_export_dict self.notification_groups = {} # DB Subnet Group db_subnet_id_list_param = self.create_cfn_parameter( param_type='List<AWS::EC2::Subnet::Id>', name='DBSubnetIdList', description='The list of subnet IDs where this database will be provisioned.', value=rds_aurora.segment + '.subnet_id_list', ) db_subnet_group_resource = troposphere.rds.DBSubnetGroup( title='DBSubnetGroup', template=self.template, DBSubnetGroupDescription=troposphere.Ref('AWS::StackName'), SubnetIds=troposphere.Ref(db_subnet_id_list_param), ) db_cluster_dict['DBSubnetGroupName'] = troposphere.Ref(db_subnet_group_resource) # DB Cluster Parameter Group if rds_aurora.cluster_parameter_group == None: # If no Cluster Parameter Group supplied then create one param_group_family = gen_vocabulary.rds_engine_versions[rds_aurora.engine][rds_aurora.engine_version]['param_group_family'] cluster_parameter_group_ref = troposphere.rds.DBClusterParameterGroup( "DBClusterParameterGroup", template=self.template, Family=param_group_family, Description=troposphere.Ref('AWS::StackName') ) else: # Use existing Parameter Group cluster_parameter_group_ref = self.create_cfn_parameter( name='DBClusterParameterGroupName', param_type='String', description='DB Cluster Parameter Group Name', value=rds_aurora.cluster_parameter_group + '.name', ) db_cluster_dict['DBClusterParameterGroupName'] = troposphere.Ref(cluster_parameter_group_ref) # Default DB Parameter Group default_dbparametergroup_resource = None need_db_pg = False default_instance = rds_aurora.default_instance for db_instance in rds_aurora.db_instances.values(): if default_instance.parameter_group == None and db_instance.parameter_group == None: need_db_pg = True if need_db_pg: # create default DB Parameter Group param_group_family = gen_vocabulary.rds_engine_versions[rds_aurora.engine][rds_aurora.engine_version]['param_group_family'] default_dbparametergroup_resource = troposphere.rds.DBParameterGroup( "DBParameterGroup", template=self.template, Family=param_group_family, Description=troposphere.Ref('AWS::StackName') ) # Enhanced Monitoring Role need_monitoring_role = False enhanced_monitoring_role_resource = None for db_instance in rds_aurora.db_instances.values(): enhanced_monitoring_interval = db_instance.get_value_or_default('enhanced_monitoring_interval_in_seconds') if enhanced_monitoring_interval != 0: need_monitoring_role = True if need_monitoring_role: enhanced_monitoring_role_resource = troposphere.iam.Role( title='MonitoringIAMRole', template=self.template, AssumeRolePolicyDocument=PolicyDocument( Statement=[ Statement( Effect=Allow, Action=[Action("sts", "AssumeRole")], Principal=Principal("Service", "monitoring.rds.amazonaws.com") ) ] ), ManagedPolicyArns=["arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"], Path="/", ) # DB Snapshot Identifier if rds_aurora.db_snapshot_identifier == '' or rds_aurora.db_snapshot_identifier == None: db_snapshot_id_enabled = False else: db_snapshot_id_enabled = True if db_snapshot_id_enabled == True: db_cluster_dict['SnapshotIdentifier'] = rds_aurora.db_snapshot_identifier # CloudWatch Export Logs if len(rds_aurora.cloudwatch_logs_exports) > 0: logs_export_list = [] if 'postgresql' in rds_aurora.cloudwatch_logs_exports: logs_export_list.append('postgresql') #if 'upgrade' in rds_aurora.cloudwatch_logs_exports: # logs_export_list.append('upgrade') db_cluster_dict['EnableCloudwatchLogsExports'] = logs_export_list # KMS-CMK key encryption if rds_aurora.enable_kms_encryption == True and db_snapshot_id_enabled == False: key_policy = Policy( Version='2012-10-17', Statement=[ Statement( Effect=Allow, Action=[Action('kms', '*'),], Principal=Principal("AWS", [f'arn:aws:iam::{self.stack.account_ctx.id}:root']), Resource=['*'], ), Statement( Effect=Allow, Action=[ awacs.kms.Encrypt, awacs.kms.Decrypt, Action('kms', 'ReEncrypt*'), Action('kms', 'GenerateDataKey*'), awacs.kms.CreateGrant, awacs.kms.ListGrants, awacs.kms.DescribeKey, ], Principal=Principal('AWS',['*']), Resource=['*'], Condition=Condition([ StringEquals({ 'kms:CallerAccount': f'{self.stack.account_ctx.id}', 'kms:ViaService': f'rds.{self.stack.aws_region}.amazonaws.com' }) ]), ), ], ) kms_key_resource = troposphere.kms.Key( title='AuroraKMSCMK', template=self.template, KeyPolicy=key_policy, ) db_cluster_dict['StorageEncrypted'] = True db_cluster_dict['KmsKeyId'] = troposphere.Ref(kms_key_resource) kms_key_alias_resource = troposphere.kms.Alias( title="AuroraKMSCMKAlias", template=self.template, AliasName=troposphere.Sub('alias/${' + rds_cluster_logical_id + '}'), TargetKeyId=troposphere.Ref(kms_key_resource), ) kms_key_alias_resource.DependsOn = rds_cluster_logical_id # Username and Passsword - only if there is no DB Snapshot Identifier if db_snapshot_id_enabled == False: db_cluster_dict['MasterUsername'] = rds_aurora.master_username if rds_aurora.secrets_password: # Password from Secrets Manager sta_logical_id = 'SecretTargetAttachmentRDS' secret_arn_param = self.create_cfn_parameter( param_type='String', name='RDSSecretARN', description='The ARN for the secret for the RDS master password.', value=rds_aurora.secrets_password + '.arn', ) secret_target_attachment_resource = troposphere.secretsmanager.SecretTargetAttachment( title=sta_logical_id, template=self.template, SecretId=troposphere.Ref(secret_arn_param), TargetId=troposphere.Ref(rds_cluster_logical_id), TargetType='AWS::RDS::DBCluster' ) secret_target_attachment_resource.DependsOn = rds_cluster_logical_id db_cluster_dict['MasterUserPassword'] = troposphere.Join( '', ['{{resolve:secretsmanager:', troposphere.Ref(secret_arn_param), ':SecretString:password}}' ] ) else: master_password_param = self.create_cfn_parameter( param_type='String', name='MasterUserPassword', description='The master user password.', value=rds_aurora.master_user_password, noecho=True, ) db_cluster_dict['MasterUserPassword'] = troposphere.Ref(master_password_param) # VPC Security Groups Ids sg_param_ref_list = [] for sg_ref in rds_aurora.security_groups: sg_hash = utils.md5sum(str_data=sg_ref) sg_param = self.create_cfn_parameter( param_type='AWS::EC2::SecurityGroup::Id', name=self.create_cfn_logical_id(f'SecurityGroup{sg_hash}'), description='VPC Security Group for DB Cluster', value=sg_ref + '.id', ) sg_param_ref_list.append(troposphere.Ref(sg_param)) if len(sg_param_ref_list) > 0: db_cluster_dict['VpcSecurityGroupIds'] = sg_param_ref_list # Backtracking if rds_aurora.backtrack_window_in_seconds > 0: db_cluster_dict['BacktrackWindow'] = rds_aurora.backtrack_window_in_seconds if rds_aurora.engine_mode == 'serverless': # Backtracking is not supported on serverless engine mode # db_cluster_dict['BacktrackWindow'] = 0 db_cluster_dict['ScalingConfiguration'] = { 'AutoPause': True, 'MinCapacity': 1, 'MaxCapacity': 32, 'SecondsUntilAutoPause': 300 } db_cluster_dict['Engine'] = 'aurora-mysql' db_cluster_res = troposphere.rds.DBCluster.from_dict( rds_cluster_logical_id, db_cluster_dict ) self.template.add_resource(db_cluster_res) # Cluster Event Notifications if hasattr(rds_aurora, 'cluster_event_notifications'): for group in rds_aurora.cluster_event_notifications.groups: notif_param = self.create_notification_param(group) event_subscription_resource = troposphere.rds.EventSubscription( title=self.create_cfn_logical_id(f"ClusterEventSubscription{group}"), template=self.template, EventCategories=rds_aurora.cluster_event_notifications.event_categories, SourceIds=[troposphere.Ref(db_cluster_res)], SnsTopicArn=troposphere.Ref(notif_param), SourceType='db-cluster', ) # DB Instance(s) for db_instance in rds_aurora.db_instances.values(): logical_name = self.create_cfn_logical_id(db_instance.name) if db_instance.external_resource == True: # Setup event notifications self.set_db_instance_event_notifications(logical_name, db_instance) # External instance only needs to output its name self.create_output( title=f'DBInstanceName{logical_name}', description=f'DB Instance Name for {logical_name}', value=db_instance.external_instance_name, ref=db_instance.paco_ref_parts + ".name", ) continue db_instance_dict = { 'DBClusterIdentifier': troposphere.Ref(db_cluster_res), 'DBInstanceClass': db_instance.get_value_or_default('db_instance_type'), 'DBSubnetGroupName': troposphere.Ref(db_subnet_group_resource), 'EnablePerformanceInsights': db_instance.get_value_or_default('enable_performance_insights'), 'Engine': rds_aurora.engine, 'PubliclyAccessible': db_instance.get_value_or_default('publicly_accessible'), 'AllowMajorVersionUpgrade': db_instance.get_value_or_default('allow_major_version_upgrade'), 'AutoMinorVersionUpgrade': db_instance.get_value_or_default('auto_minor_version_upgrade'), } # Enhanced Monitoring enhanced_monitoring_interval = db_instance.get_value_or_default('enhanced_monitoring_interval_in_seconds') if enhanced_monitoring_interval != 0: db_instance_dict['MonitoringInterval'] = enhanced_monitoring_interval db_instance_dict['MonitoringRoleArn'] = troposphere.GetAtt(enhanced_monitoring_role_resource, "Arn") if db_instance.availability_zone != None: subnet_id_ref = f'{rds_aurora.segment}.az{db_instance.availability_zone}.availability_zone' db_instance_subnet_param = self.create_cfn_parameter( param_type='String', name=f'DBInstanceAZ{logical_name}', description=f'Subnet where DB Instance {logical_name} is provisioned', value=subnet_id_ref, ) db_instance_dict['AvailabilityZone'] = troposphere.Ref(db_instance_subnet_param) # DB Parameter Group if default_instance.parameter_group == None and db_instance.parameter_group == None: dbparametergroup_resource = default_dbparametergroup_resource elif db_instance.parameter_group != None: # Use instance-specific DB Parameter Group dbparametergroup_resource = self.create_cfn_parameter( name=f'DBParameterGroupName{logical_name}', param_type='String', description='DB Parameter Group Name', value=db_instance.parameter_group + '.name', ) else: # Use default DB Parameter Group dbparametergroup_resource = self.create_cfn_parameter( name=f'DBParameterGroupName{logical_name}', param_type='String', description='DB Parameter Group Name', value=default_instance.parameter_group + '.name', ) db_instance_dict['DBParameterGroupName'] = troposphere.Ref(dbparametergroup_resource) db_instance_resource = troposphere.rds.DBInstance.from_dict( f'DBInstance{logical_name}', db_instance_dict ) self.template.add_resource(db_instance_resource) # DB Event Notifications self.set_db_instance_event_notifications(logical_name, db_instance, db_instance_resource) # DB Instance Outputs self.create_output( title=f'DBInstanceName{logical_name}', description=f'DB Instance Name for {logical_name}', value=troposphere.Ref(db_instance_resource), ref=db_instance.paco_ref_parts + ".name", ) # DB Cluster Outputs self.create_output( title='DBClusterName', description='DB Cluster Name', value=troposphere.Ref(db_cluster_res), ref=self.resource.paco_ref_parts + ".name", ) self.create_output( title='ClusterEndpointAddress', description='Cluster Endpoint Address', value=troposphere.GetAtt(db_cluster_res, 'Endpoint.Address'), ref=self.resource.paco_ref_parts + ".endpoint.address", ) self.create_output( title='ClusterEndpointPort', description='Cluster Endpoint Port', value=troposphere.GetAtt(db_cluster_res, 'Endpoint.Port'), ref=self.resource.paco_ref_parts + ".endpoint.port", ) if rds_aurora.engine_mode != 'serverless': self.create_output( title='ClusterReadEndpointAddress', description='Cluster ReadEndpoint Address', value=troposphere.GetAtt(db_cluster_res, 'ReadEndpoint.Address'), ref=self.resource.paco_ref_parts + ".readendpoint.address", ) # DNS - Route53 Record Set if rds_aurora.is_dns_enabled() == True: route53_ctl = self.paco_ctx.get_controller('route53') for dns in rds_aurora.dns: route53_ctl.add_record_set( self.account_ctx, self.aws_region, rds_aurora, enabled=rds_aurora.is_enabled(), dns=dns, record_set_type='CNAME', resource_records=[rds_aurora.paco_ref + '.endpoint.address'], stack_group=self.stack.stack_group, async_stack_provision=True ) for read_dns in rds_aurora.read_dns: route53_ctl.add_record_set( self.account_ctx, self.aws_region, rds_aurora, enabled=rds_aurora.is_enabled(), dns=read_dns, record_set_type='CNAME', resource_records=[rds_aurora.paco_ref + '.readendpoint.address'], stack_group=self.stack.stack_group, async_stack_provision=True )
'Effect': 'Allow', 'Principal': { 'Service': ['ec2.amazonaws.com'] }, 'Action': ['sts:AssumeRole'] }] }, Policies=[ iam.Policy(PolicyName='GrantCloudwatch', PolicyDocument={ "Version": "2012-10-17", "Statement": [ Statement(Effect=Allow, Action=[ Action('cloudwatch', '*'), ], Resource=['*']), ] }), iam.Policy(PolicyName='GrantLogs', PolicyDocument={ "Version": "2012-10-17", "Statement": [ Statement(Effect=Allow, Action=[ Action('logs', '*'), ], Resource=["arn:aws:logs:*:*:*"]), ]
't2.medium', 't2.large', ], ConstraintDescription='must be a valid EC2 T2 instance type.', )) t.add_resource( IAMPolicy("MonitoringPolicy", PolicyName="AllowSendingDataForMonitoring", PolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[ Action("cloudwatch", "Put*"), Action("logs", "Create*"), Action("logs", "Put*"), Action("logs", "Describe*"), Action("events", "Put*"), Action("firehouse", "Put*"), Action("autoscaling", "DescribeAutoScalingInstances"), ], Resource=["*"]) ]), Roles=[Ref("Role")])) t.add_resource( ec2.SecurityGroup( "SecurityGroup", GroupDescription="Allow SSH and TCP/{} access".format(ApplicationPort), SecurityGroupIngress=[ ec2.SecurityGroupRule( IpProtocol="tcp",
def create_template(self): t = self.template bucket_arn = Sub("arn:aws:s3:::${StackerBucket}*") cloudformation_scope = Sub( "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:" "stack/${StackerNamespace}-*") changeset_scope = "*" # This represents the precise IAM permissions that stacker itself # needs. stacker_policy = iam.Policy( PolicyName="Stacker", PolicyDocument=Policy( Statement=[ Statement( Effect="Allow", Resource=[bucket_arn], Action=[ awacs.s3.ListBucket, awacs.s3.GetBucketLocation, awacs.s3.CreateBucket]), Statement( Effect="Allow", Resource=[bucket_arn], Action=[ awacs.s3.GetObject, awacs.s3.GetObjectAcl, awacs.s3.PutObject, awacs.s3.PutObjectAcl]), Statement( Effect="Allow", Resource=[changeset_scope], Action=[ awacs.cloudformation.DescribeChangeSet, awacs.cloudformation.ExecuteChangeSet, ]), 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.DeleteStack, awacs.cloudformation.CreateStack, awacs.cloudformation.UpdateStack, awacs.cloudformation.DescribeStacks])])) user = t.add_resource( iam.User( "FunctionalTestUser", Policies=[ stacker_policy])) key = t.add_resource( iam.AccessKey( "FunctionalTestKey", Serial=1, UserName=Ref(user))) t.add_output(Output("User", Value=Ref(user))) t.add_output(Output("AccessKeyId", Value=Ref(key))) t.add_output( Output( "SecretAccessKey", Value=GetAtt("FunctionalTestKey", "SecretAccessKey")))
from troposphere import Template from troposphere.iam import Role from awacs.aws import Allow, Statement, Principal, Policy from awacs.sts import AssumeRole t = Template() t.add_resource( Role('Role2', AssumeRolePolicyDocument=Policy(Statement=[ Statement(Effect=Allow, Action=[AssumeRole], Principal=Principal('Service', ['ec2.amazonaws.com'])) ]))) print(t.to_yaml())
ud = Base64(Join('\n', [ "#!/bin/bash", "yum install --enablerepo=epel -y git", "pip install ansible", AnsiblePullCmd, "echo '*/10 * * * * {}' > /etc/cron.d/ansible-ull".format(AnsiblePullCmd) ])) t.add_resource(Role( "Role", AssumeRolePolicyDocument=Policy( Statement=[ Statement( Effect=Allow, Action=[AssumeRole], Principal=Principal("Service", ["ec2.amazonaws.com"]) ) ] ) )) t.add_resource(InstanceProfile( "InstanceProfile", Path="/", Roles=[Ref("Role")] )) t.add_resource(ec2.Instance( "instance", ImageId="ami-07edebf3b6affeea1",
def make_simple_assume_statement(*principals): return Statement( Principal=Principal('Service', principals), Effect=Allow, Action=[sts.AssumeRole] )
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 IAM policies - V1.0.0') # Resources nodeinstancerole = template.add_resource( iam.Role( 'NodeInstanceRole', AssumeRolePolicyDocument=make_simple_assume_policy( 'ec2.amazonaws.com'), ManagedPolicyArns=[ IAM_POLICY_ARN_PREFIX + i for i in [ 'AmazonEKSWorkerNodePolicy', 'AmazonEKS_CNI_Policy', 'AmazonEC2ContainerRegistryReadOnly', # SSM agent not shipped ootb # 'AmazonSSMManagedInstanceCore' ] ])) template.add_output( Output('NodeInstanceRole', Description='The node instance role name', Value=nodeinstancerole.ref())) template.add_output( Output('NodeInstanceRoleArn', Description='The node instance role ARN', Value=nodeinstancerole.get_att('Arn'))) nodeinstanceprofile = template.add_resource( iam.InstanceProfile('NodeInstanceProfile', Path='/', Roles=[nodeinstancerole.ref()])) template.add_output( Output('NodeInstanceProfile', Description='The node instance profile', Value=nodeinstanceprofile.ref())) template.add_output( Output('NodeInstanceProfileArn', Description='The node instance profile ARN', Value=nodeinstanceprofile.get_att('Arn'))) template.add_resource( iam.Role( 'ClusterAutoScalerInstanceRole', AssumeRolePolicyDocument=make_simple_assume_policy( 'ec2.amazonaws.com'), Policies=[ iam.Policy( PolicyName='cluster-autoscaler', PolicyDocument=PolicyDocument( Version='2012-10-17', Statement=[ Statement( Action=[ awacs.autoscaling. DescribeAutoScalingGroups, # noqa awacs.autoscaling. DescribeAutoScalingInstances, # noqa awacs.autoscaling.DescribeTags, awacs.autoscaling. SetDesiredCapacity, # noqa awacs.autoscaling. TerminateInstanceInAutoScalingGroup ], # noqa pylint: disable=line-too-long Effect=Allow, Resource=['*']) ])) ]))
def generate_cf_template(): """ Returns an entire CloudFormation stack by using troposphere to construct each piece """ # Header of CloudFormation template t = Template() t.add_version("2010-09-09") t.add_description("Lambda Chat AWS Resources") # Paramters description = "should match [0-9]+-[a-z0-9]+.apps.googleusercontent.com" google_oauth_client_id = t.add_parameter(Parameter( "GoogleOAuthClientID", AllowedPattern="[0-9]+-[a-z0-9]+.apps.googleusercontent.com", Type="String", Description="The Client ID of your Google project", ConstraintDescription=description )) website_s3_bucket_name = t.add_parameter(Parameter( "WebsiteS3BucketName", AllowedPattern="[a-zA-Z0-9\-]*", Type="String", Description="Name of S3 bucket to store the website in", ConstraintDescription="can contain only alphanumeric characters and dashes.", )) # The SNS topic the website will publish chat messages to website_sns_topic = t.add_resource(sns.Topic( 'WebsiteSnsTopic', TopicName='lambda-chat', DisplayName='Lambda Chat' )) t.add_output(Output( "WebsiteSnsTopic", Description="sns_topic_arn", Value=Ref(website_sns_topic), )) # The IAM Role and Policy the website will assume to publish to SNS website_role = t.add_resource(iam.Role( "WebsiteRole", Path="/", AssumeRolePolicyDocument=Policy( Statement=[ Statement( Effect=Allow, Action=[Action("sts", "AssumeRoleWithWebIdentity")], Principal=Principal("Federated", "accounts.google.com"), Condition=Condition( StringEquals( "accounts.google.com:aud", Ref(google_oauth_client_id) ) ), ), ], ), )) t.add_resource(iam.PolicyType( "WebsitePolicy", PolicyName="lambda-chat-website-policy", Roles=[Ref(website_role)], PolicyDocument=Policy( Version="2012-10-17", Statement=[ Statement( Effect=Allow, Action=[Action("sns", "Publish")], Resource=[ Ref(website_sns_topic) ], ), ], ) )) t.add_output(Output( "WebsiteRole", Description="website_iam_role_arn", Value=GetAtt(website_role, "Arn"), )) website_bucket = t.add_resource(s3.Bucket( 'WebsiteS3Bucket', BucketName=Ref(website_s3_bucket_name), WebsiteConfiguration=s3.WebsiteConfiguration( ErrorDocument="error.html", IndexDocument="index.html" ) )) t.add_output(Output( "S3Bucket", Description="s3_bucket", Value=Ref(website_bucket), )) t.add_resource(s3.BucketPolicy( 'WebsiteS3BucketPolicy', Bucket=Ref(website_bucket), PolicyDocument={ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicAccess", "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": [{ "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "WebsiteS3Bucket", }, "/*" ] ] }] } ] } )) return t
STACK_NAME = cfg['ec2']['stack_name'] SERVER_NAME = cfg['ec2']['server_name'] STREAM_NAME = cfg['ec2']['stream_name'] TWITTER_KEYWORDS = cfg['ec2']['twitter_keywords'] deployment_commithash = '8aebd8fc0c3b62da8bf881b8678467492801022d' template = Template() description = 'Twitter Producer Stack' template.add_description(description) template.add_version('2010-09-09') # instance role instance_policy_doc = Policy(Statement=[ Statement(Sid='KinesisAccess', Effect=Allow, Action=[Action('kinesis', '*')], Resource=[ 'arn:aws:kinesis:eu-west-1:749785218022:stream/DevStreamES' ]), Statement(Sid='LogsAccess', Effect=Allow, Action=[Action('logs', '*')], Resource=['*']), Statement(Sid='ReadS3DeploymentBucket', Effect=Allow, Action=[Action('s3', 'Get*'), Action('s3', 'List*')], Resource=[ s3.ARN('nicor-dev'), s3.ARN('nicor-dev/*'), ]) ])
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) ) )