def create_web_route(self): web_route_table = self.tpl.add_resource( ec2.RouteTable( 'WebRouteTable', VpcId=self.vpc_id, Tags=[ Tag('Name', f'{self.prefix}-{self.project}-WebRouteTable') ])) # Webのルートテーブルの論理IDをアウトプット self.tpl.add_output( Output('WebRouteTableId', Description='Web Route Table Id', Value=Ref(web_route_table))) # Webのルートテーブルにルートを追加 self.tpl.add_resource( ec2.Route('WebRoute', RouteTableId=Ref(web_route_table), DestinationCidrBlock='0.0.0.0/0', GatewayId=self.igw_id)) # Webサブネットにルートテーブルを紐つける for index, subnet_id in enumerate(self.web_subnet_list): self.tpl.add_resource( ec2.SubnetRouteTableAssociation( f'WebSubnet{index}RouteTable', RouteTableId=Ref(web_route_table), SubnetId=subnet_id))
def test_add_parameter_resource_output(self): from troposphere_mate import apigateway tpl = Template() param_project_name = Parameter("ProjectName", Type="String") rest_api = apigateway.RestApi( "RestApi", template=tpl, Name=Ref(param_project_name), EndpointConfiguration=apigateway.EndpointConfiguration( Types=["REGIONAL"])) output_rest_api_id = Output("RestApiId", Value=Ref(rest_api)) # test ignore_duplicate argument tpl.add_parameter(param_project_name) with raises(ValueError): tpl.add_parameter(param_project_name) tpl.add_parameter(param_project_name, ignore_duplicate=True) with raises(ValueError): tpl.add_resource(rest_api) tpl.add_resource(rest_api, ignore_duplicate=True) tpl.add_output(output_rest_api_id) with raises(ValueError): tpl.add_output(output_rest_api_id) tpl.add_output(output_rest_api_id, ignore_duplicate=True)
def apigw_authorizer_lbd_permission_aws_object( self) -> awslambda.Permission: if self._apigw_authorizer_lbd_permission_aws_object_cache is NOTHING: apigw_authorizer_lbd_permission_logic_id = "LbdPermission{}".format( self.apigw_authorizer_logic_id) apigw_authorizer_lbd_permission = awslambda.Permission( title=apigw_authorizer_lbd_permission_logic_id, Action="lambda:InvokeFunction", FunctionName=GetAtt(self.lbd_func_aws_object, "Arn"), Principal="apigateway.amazonaws.com", SourceArn=Sub( "arn:aws:execute-api:${Region}:${AccountId}:${RestApiId}/authorizers/${AuthorizerId}", { "Region": { "Ref": "AWS::Region" }, "AccountId": { "Ref": "AWS::AccountId" }, "RestApiId": Ref(self.apigw_restapi), "AuthorizerId": Ref(self.apigw_authorizer_aws_object), }), DependsOn=[ self.apigw_authorizer_aws_object, self.lbd_func_aws_object, ]) self._apigw_authorizer_lbd_permission_aws_object_cache = apigw_authorizer_lbd_permission return self._apigw_authorizer_lbd_permission_aws_object_cache
def create_vpc_base(self): # VPCを作成 vpc = self.tpl.add_resource( ec2.VPC('Vpc', CidrBlock=self.vpc_cidr_block, EnableDnsSupport=True, EnableDnsHostnames=True, Tags=[Tag('Name', f'{self.prefix}-{self.project}-vpc')])) # VPCの論理IDをoutputする self.tpl.add_output( Output('VpcId', Description='VpcId', Value=Ref(vpc))) # VPCのCidrBlockをoutputする self.tpl.add_output( Output('VpcCidr', Description='VPC Cidr Block', Value=self.vpc_cidr_block)) # InternetGatewayを作成 igw = self.tpl.add_resource(ec2.InternetGateway('Igw')) # InternetGatewayの論理IDをoutputする self.tpl.add_output( Output('IgwId', Description='Internet Gateway Id', Value=Ref(igw))) # InternetGatewayをVPCにアタッチする self.tpl.add_resource( ec2.VPCGatewayAttachment('VpcIgwAttachment', InternetGatewayId=Ref(igw), VpcId=Ref(vpc))) return vpc, igw
def s3_event_bucket_name_for_cf(self) -> typing.Union[str, Sub]: """ ``s3.Bucket.BucketName`` field value. ``AWS::AccountId`` is included as prefix. :rtype: Sub **中文文档** 生成存储桶的名称. 由于存储桶的名字是全球唯一的, 并且不分 Region, 所以需要加上 AWS Account Id 作为名字的前缀, 以避免冲突. """ if self.s3_event_bucket_basename is NOTHING: raise TypeError if isinstance(self.s3_event_bucket_basename, str): return Sub( "${AccountId}-${EnvName}-${BucketBasename}", { "AccountId": Ref(AWS_ACCOUNT_ID), "EnvName": Ref(self.param_env_name), "BucketBasename": self.s3_event_bucket_basename, }, ) else: raise TypeError
def create_cloudfront_s3_origin( self, bucket_name, oai: cf.CloudFrontOriginAccessIdentity) -> cf.Origin: return cf.Origin( Id=f'S3Origin-{bucket_name}', DomainName=Join( '', [f'{bucket_name}.s3-', Ref(AWS_REGION), '.amazonaws.com']), S3OriginConfig=cf.S3OriginConfig(OriginAccessIdentity=Join( '', ['origin-access-identity/cloudfront/', Ref(oai)])))
def associate(self, lbd_func, sg, *args, **kwargs): """ Assign Lambda Function a Security Group Rule. """ try: lbd_func.VpcConfig.SecurityGroupIds.append(Ref(sg)) except AttributeError: lbd_func.VpcConfig = awslambda.VPCConfig(SubnetIds=[], SecurityGroupIds=[ Ref(sg), ]) x_depends_on_y(lbd_func, sg)
def create_template(self): # ALBを作成する alb = self.tpl.add_resource( elb.LoadBalancer( 'LoadBarancer', Name=f'{self.prefix}-{self.project}-alb', LoadBalancerAttributes=[ elb.LoadBalancerAttributes(Key='access_logs.s3.enabled', Value='true'), elb.LoadBalancerAttributes(Key='access_logs.s3.bucket', Value=self.elb_log_bucket) ], Scheme='internet-facing', SecurityGroups=self.security_group_list, Subnets=self.subnet_id_list)) # ALBの論理IDをアウトプット self.tpl.add_output( Output('LoadBalancer', Description='ALB ARN', Value=Ref(alb))) # ターゲットグループを作成 target_group = self.tpl.add_resource( elb.TargetGroup('TargetGroup', HealthCheckPath='/', HealthCheckPort='80', HealthCheckProtocol='HTTP', Matcher=elb.Matcher(HttpCode='200'), Name=f'{self.prefix}-{self.project}-target-group', Port=80, Protocol='HTTP', TargetType='instance', VpcId=self.vpc_id)) # ターゲットグループの論理IDをアウトプット self.tpl.add_output( Output('TargetGroupId', Description='TargetGroup Id', Value=Ref(target_group))) # リスナーを作成 self.tpl.add_resource( elb.Listener('Listener', DefaultActions=[ elb.Action( Type='forward', TargetGroupArn=Ref(target_group), ) ], LoadBalancerArn=Ref(alb), Port=80, Protocol='HTTP'))
def associate(self, lbd_func, subnet, *args, **kwargs): """ Deploy Lambda Function to VPC Subnet. """ try: lbd_func.VpcConfig.SubnetIds.append(Ref(subnet)) except AttributeError: lbd_func.VpcConfig = awslambda.VPCConfig( SubnetIds=[ Ref(subnet), ], SecurityGroupIds=[], ) x_depends_on_y(lbd_func, subnet)
def associate(self, lbd_permission, lbd_func, api_method, **kwargs): lbd_permission.FunctionName = GetAtt(lbd_func, "Arn") lbd_permission.Action = "lambda:InvokeFunction" lbd_permission.Principal = "apigateway.amazonaws.com" try: lbd_permission.SourceArn = Sub( "arn:aws:execute-api:${Region}:${AccountId}:${RestApiId}/*/${HttpMethod}/${ResourcePath}", { "Region": {"Ref": "AWS::Region"}, "AccountId": {"Ref": "AWS::AccountId"}, "RestApiId": Ref(api_method.RestApiId), "HttpMethod": api_method.HttpMethod, "ResourcePath": api_method.Metadata[TROPOSPHERE_METADATA_FIELD_NAME][ ResourceLevelField.ApiResource.FULL_PATH] } ) except: pass x_depends_on_y(lbd_permission, lbd_func) x_depends_on_y(lbd_permission, api_method)
def test_from_dict(self): from troposphere_mate.apigateway import RestApi tpl = Template() rest_api = RestApi( "RestApi", template=tpl, Metadata={"description": "a rest api"}, Name="MyApi", ) output_rest_api_id = Output("RestApiId", Value=Ref(rest_api), DependsOn=rest_api) tpl.add_output(output_rest_api_id) dct = tpl.to_dict() # print(tpl.to_json()) tpl = Template.from_dict(dct) tpl.remove_resource_by_label(label="na") assert tpl.to_dict() == dct assert isinstance(tpl.resources["RestApi"], RestApi) assert isinstance(tpl.outputs["RestApiId"], Output) assert getattr( tpl.outputs[output_rest_api_id.title], mtdt.TemplateLevelField.OUTPUTS_DEPENDS_ON, ) == [ rest_api.title, ] assert tpl.to_dict() == dct
def create_launch_template(self): web_sg_list = [self.web_sg_id] launch_template = self.tpl.add_resource( ec2.LaunchTemplate( 'LaunchTemplate', LaunchTemplateName=f'{self.prefix}-{self.project}-LaunchTemplate', LaunchTemplateData=ec2.LaunchTemplateData( ImageId=self.image_id, InstanceType=self.instance_type, KeyName=self.key_name, SecurityGroupIds=web_sg_list ) ) ) # 起動テンプレートの論理IDをアウトプット self.tpl.add_output( Output( 'LaunchTemplateId', Description='LaunchTemplate Id', Value=Ref(launch_template) ) ) # 起動テンプレートのバージョンをアウトプット self.tpl.add_output( Output( 'LaunchTemplateLatestVersion', Description='LaunchTemplate Latest Version', Value=GetAtt(launch_template, 'LatestVersionNumber') ) )
def test_remove_parameter_and_output(self): tpl = Template() p1 = Parameter("P1", Type="string") o1 = Output("O1", Value=Ref("P1")) # before state tpl.add_parameter(p1) tpl.add_output(o1) assert len(tpl.parameters) == 1 assert len(tpl.outputs) == 1 # test remove by object tpl.remove_parameter(p1) tpl.remove_output(o1) assert len(tpl.parameters) == 0 assert len(tpl.outputs) == 0 # before state tpl.add_parameter(p1) tpl.add_output(o1) assert len(tpl.parameters) == 1 assert len(tpl.outputs) == 1 # test remove by str tpl.remove_parameter(p1.title) tpl.remove_output(o1.title) assert len(tpl.parameters) == 0 assert len(tpl.outputs) == 0
def create_s3_oai(self) -> cf.CloudFrontOriginAccessIdentity: return self.template.add_resource( cf.CloudFrontOriginAccessIdentity( 'FrontS3OAI', CloudFrontOriginAccessIdentityConfig=cf. CloudFrontOriginAccessIdentityConfig( Comment=Ref(AWS_STACK_NAME)), ))
def test_update_tags(self): from troposphere_mate import ec2 # overwrite=False works tpl = Template() my_vpc = ec2.VPC("MyVPC", template=tpl, CidrBlock="10.0.0.0/16", Tags=Tags(Creator="Alice")) my_sg = ec2.SecurityGroup( "MySG", template=tpl, GroupDescription="My", GroupName="MySG", VpcId=Ref(my_vpc), ) my_subnet = ec2.Subnet( "MySubnet", template=tpl, CidrBlock="10.0.1.0/24", VpcId=Ref(my_vpc), ) def get_name(resource, project): if resource.resource_type == "AWS::EC2::SecurityGroup": return "{}/sg/{}".format(project, resource.GroupName) tpl.update_tags(dict(Project="my-project"), overwrite=False) tpl.update_tags(tags_dct=dict( Name=functools.partial(get_name, project="my-project"), Creator="Bob", ), overwrite=False) assert tags_list_to_dct( tpl.to_dict()["Resources"]["MyVPC"]["Properties"]["Tags"]) == dict( Project="my-project", Creator="Alice", ) assert tags_list_to_dct( tpl.to_dict()["Resources"]["MySG"]["Properties"]["Tags"]) == dict( Project="my-project", Name="my-project/sg/MySG", Creator="Bob", )
def create_template(self): var = self.get_variables() project_id = var.get('ProjectId') environment = var.get('Environment') assets_bucket_name = var.get('AssetsBucketName') self.template.description = 'S3 Bucket Stack' # Front S3 Bucket assets_bucket = self.template.add_resource( s3.Bucket( 'AssetsBucket', #primary name BucketName=Join('-', [assets_bucket_name, Ref(AWS_ACCOUNT_ID)]), DeletionPolicy=Retain)) self.template.add_output(Output('AssetsBucketName', Ref(assets_bucket)))
def associate(self, lbd_event_map, lbd_func, dynamodb_table, *args, **kwargs): """ Use DynamoDB Table Stream to trigger Lambda Function """ lbd_event_map.FunctionName = Ref(lbd_func) lbd_event_map.EventSourceArn = GetAtt(dynamodb_table, "StreamArn") x_depends_on_y(lbd_event_map, lbd_func) x_depends_on_y(lbd_event_map, dynamodb_table)
def associate(self, lbd_event_map, lbd_func, kinesis_stream, *args, **kwargs): """ Use Kinesis Stream to trigger Lambda Function """ lbd_event_map.FunctionName = Ref(lbd_func) lbd_event_map.EventSourceArn = GetAtt(kinesis_stream, "Arn") x_depends_on_y(lbd_event_map, lbd_func) x_depends_on_y(lbd_event_map, kinesis_stream)
def associate(self, lbd_event_map, lbd_func, sqs_queue, *args, **kwargs): """ Use SQS Queue to trigger Lambda Function """ lbd_event_map.FunctionName = Ref(lbd_func) lbd_event_map.EventSourceArn = GetAtt(sqs_queue, "Arn") x_depends_on_y(lbd_event_map, lbd_func) x_depends_on_y(lbd_event_map, sqs_queue)
def test_mutable_aws_object(): """ 确定 ``mate.AWSObject`` 变化时, 对应的 ``mate.AWSObject.aws_object`` 也应该 跟着变化. """ from troposphere_mate import iam tpl = Template() my_policy = iam.ManagedPolicy( title="MyPolicy", template=tpl, PolicyDocument={}, ) my_role = iam.Role( title="MyRole", template=tpl, RoleName="my-role", AssumeRolePolicyDocument={}, ) assert tpl.to_dict( )["Resources"]["MyRole"]["Properties"]["RoleName"] == "my-role" assert "ManagedPolicyArns" not in tpl.to_dict( )["Resources"]["MyRole"]["Properties"] my_role.RoleName = "my-role-two" my_role.ManagedPolicyArns = [Ref(my_policy)] assert tpl.to_dict( )["Resources"]["MyRole"]["Properties"]["RoleName"] == "my-role-two" assert tpl.to_dict( )["Resources"]["MyRole"]["Properties"]["ManagedPolicyArns"] == [{ "Ref": "MyPolicy" }] outputs = [ Output( "MyRolePath", Value=GetAtt(my_role, "Path"), DependsOn=my_role, ) ] for output in outputs: tpl.add_output(output) assert tpl.to_dict()["Outputs"]["MyRolePath"]["Value"] == { "Fn::GetAtt": ["MyRole", "Path"] } dct = tpl.to_dict() tpl2 = Template() tpl2.add_resource(my_policy) tpl2.add_resource(my_role) for output in outputs: tpl2.add_output(output) dct2 = tpl2.to_dict() assert dct == dct2
def apigw_resource_aws_object(self) -> apigateway.Resource: if self._apigw_resource_aws_object_cache is NOTHING: apigw_resource = apigateway.Resource( self.apigw_resource_logic_id, RestApiId=Ref(self.apigw_restapi), ParentId=self.apigw_resource_parent_id, PathPart=self.apigw_resource_path_part, DependsOn=[self.apigw_restapi], ) self._apigw_resource_aws_object_cache = apigw_resource return self._apigw_resource_aws_object_cache
def create_subnets(self, vpc, prefix, addr_list): subnets = [] for index, addr in enumerate(addr_list): subnets.append( self.tpl.add_resource( ec2.Subnet(f'{prefix}Subnet{index}', CidrBlock=addr, VpcId=Ref(vpc), AvailabilityZone=Select(index % get_az_count(), GetAZs( Ref(AWS_REGION))), Tags=[Tag('Name', f'{prefix}Subnet{index}')]))) # サブネットの論理IDをアウトプットする self.tpl.add_output( Output(f'{prefix}SubnetList', Description=f'{prefix} subnet list', Value=Join(',', [Ref(subnet) for subnet in subnets]))) return subnets
def test_init(self): rest_api = apigateway.RestApi("RestApi", Name="MyRestApi") output_rest_api_id = Output( "RestApiId", Value=Ref(rest_api), DependsOn=rest_api, ) assert output_rest_api_id.depends_on_resources == [ "RestApi", ] output_rest_api_id = Output( "RestApiId", Value=Ref(rest_api), DependsOn=[ rest_api, ], ) assert output_rest_api_id.depends_on_resources == [ "RestApi", ]
def associate(self, lbd_permission, lbd_func, api_authorizer, rest_api, authorizer_type_is_token=True, token_authorizer_header="auth", **kwargs): lbd_permission.FunctionName = GetAtt(lbd_func, "Arn") lbd_permission.Action = "lambda:InvokeFunction" lbd_permission.Principal = "apigateway.amazonaws.com" lbd_permission.SourceArn = Sub( "arn:aws:execute-api:${Region}:${AccountId}:${RestApiId}/authorizers/${AuthorizerId}", { "Region": {"Ref": "AWS::Region"}, "AccountId": {"Ref": "AWS::AccountId"}, "RestApiId": {"Ref": rest_api}, "AuthorizerId": Ref(api_authorizer), } ) if authorizer_type_is_token: api_authorizer.Type = "TOKEN" api_authorizer.IdentitySource = "method.request.header.{}".format( token_authorizer_header ) api_authorizer.RestApiId = Ref(rest_api) api_authorizer.AuthorizerUri = Sub( "arn:aws:apigateway:${Region}:lambda:path/2015-03-31/functions/${AuthorizerFunctionArn}/invocations", { "Region": {"Ref": "AWS::Region"}, "AuthorizerFunctionArn": GetAtt(lbd_func, "Arn"), } ) x_depends_on_y(api_authorizer, lbd_func) x_depends_on_y(api_authorizer, rest_api) x_depends_on_y(lbd_permission, lbd_func) x_depends_on_y(lbd_permission, api_authorizer) x_depends_on_y(lbd_permission, rest_api)
def create_db_route(self): db_route_table = self.tpl.add_resource( ec2.RouteTable( 'DBRouteTable', VpcId=self.vpc_id, Tags=[ Tag('Name', f'{self.prefix}-{self.project}-DBRouteTable') ])) # DBのルートテーブルの論理IDをアウトプット self.tpl.add_output( Output('DBRouteTableId', Description='DB Route Table Id', Value=Ref(db_route_table))) # DBのルートテーブルをサブネットに紐つける for index, subnet_id in enumerate(self.db_subnet_list): self.tpl.add_resource( ec2.SubnetRouteTableAssociation( f'DBSubnet{index}RouteTable', RouteTableId=Ref(db_route_table), SubnetId=subnet_id))
def __init__(self): self.tpl = Template() self.param_env_name = Parameter("EnvName", Type="String") self.rest_api_x = apigateway.RestApi( "RestApiX", template=self.tpl, Name="MyRestApiX", ) self.rest_api_y = apigateway.RestApi( "RestApiY", template=self.tpl, Name="MyRestApiY", DependsOn=self.rest_api_x, ) self.rest_api_z = apigateway.RestApi("RestApiZ", template=self.tpl, Name="MyRestApiZ", DependsOn=self.rest_api_y) self.output_rest_api_x_id = Output( "RestApiXId", Value=Ref(self.rest_api_x), DependsOn=self.rest_api_x, ) self.tpl.add_output(self.output_rest_api_x_id) self.output_rest_api_y_id = Output( "RestApiYId", Value=Ref(self.rest_api_y), DependsOn=self.rest_api_y, ) self.tpl.add_output(self.output_rest_api_y_id) self.output_rest_api_z_id = Output( "RestApiZId", Value=Ref(self.rest_api_z), DependsOn=self.rest_api_z, ) self.tpl.add_output(self.output_rest_api_z_id)
def associate(self, api_resource1, api_resource2, **kwargs): api_resource1.ParentId = Ref(api_resource2) metadata = initiate_default_resource_metadata(api_resource1) try: metadata[TROPOSPHERE_METADATA_FIELD_NAME][ ResourceLevelField.ApiResource.FULL_PATH] = "{}/{}".format( api_resource2.Metadata[TROPOSPHERE_METADATA_FIELD_NAME] [ResourceLevelField.ApiResource.FULL_PATH], api_resource1.PathPart) except: pass api_resource1.Metadata = metadata x_depends_on_y(api_resource1, api_resource2)
def apigw_method_authorizer_id_for_cf(self): """ Resolve the value that will be assigned to ``apigateway.Method.AuthorizerId`` property. """ if self.apigw_method_authorizer is NOTHING: raise TypeError if isinstance(self.apigw_method_authorizer, (apigateway.Authorizer, Parameter)): return Ref(self.apigw_method_authorizer) elif isinstance(self.apigw_method_authorizer, (Ref, GetAtt, ImportValue)): return self.apigw_method_authorizer elif isinstance(self.apigw_method_authorizer, str): # hard coded authorizer id (from console) if len(self.apigw_method_authorizer) \ and len(set(self.apigw_method_authorizer) \ .difference(set(string.ascii_lowercase + string.digits))): return self.apigw_method_authorizer # hard coded apigateway.Authorizer logic id else: return Ref(self.apigw_method_authorizer) else: raise TypeError
def associate(self, api_resource, rest_api, has_parent_resource=True, **kwargs): api_resource.RestApiId = Ref(rest_api) if has_parent_resource is False: api_resource.ParentId = GetAtt(rest_api, "RootResourceId") metadata = initiate_default_resource_metadata(api_resource) metadata[TROPOSPHERE_METADATA_FIELD_NAME][ ResourceLevelField.ApiResource. FULL_PATH] = api_resource.PathPart api_resource.Metadata = metadata x_depends_on_y(api_resource, rest_api)
def associate(self, api_method, api_resource, **kwargs): try: api_method.RestApiId = api_resource.RestApiId except: pass api_method.ResourceId = Ref(api_resource) metadata = initiate_default_resource_metadata(api_method) try: metadata[TROPOSPHERE_METADATA_FIELD_NAME][ResourceLevelField.ApiResource.FULL_PATH] = \ api_resource.Metadata[TROPOSPHERE_METADATA_FIELD_NAME][ResourceLevelField.ApiResource.FULL_PATH] except: pass api_method.Metadata = metadata x_depends_on_y(api_method, api_resource)