AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: > This template creates the required custom resources. Parameters: LambdaRuntime: Type: String Default: python3.7 Description: Python macro runtime Resources: ### ## Macro transform # Macro: Type: AWS::CloudFormation::Macro Properties: Name: sqlserverdevfabric-macro Description: Processes the template and creates multiple instances of the Service resource FunctionName: !Ref AWSMacroFunction AWSMacroFunction: Type: AWS::Serverless::Function Properties: FunctionName: sqlserverdevfabric-lambda-macro CodeUri: src_macro Handler: index.handler Runtime: !Ref LambdaRuntime Timeout: 360 Role: !GetAtt AWSMacroFunctionExecutionRole.Arn AWSMacroFunctionExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Version: '2012-10-17' Path: "/" Policies: - PolicyDocument: Statement: - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: arn:aws:logs:*:*:* Version: '2012-10-17' PolicyName: !Sub ${AWS::StackName}-AWSMacroFunction-CW ### ## Subnet checker # SubnetCheckerFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: !Ref LambdaRuntime Timeout: 360 Role: !GetAtt SubnetCheckerFunctionExecutionRole.Arn InlineCode: | import boto3 import cfnresponse def error_and_exit(error): print(error) raise Exception(error) def handler(event, context): try: the_event = event['RequestType'] response_data = {} phy_res_id = str(hash(event['StackId'] + event['LogicalResourceId']))[1:] print("Event dump: %s" % str(event)) print("Request type: ", str(the_event)) if the_event in ('Create', 'Update'): input_subnet_list = event['ResourceProperties']['Subnets'] the_vpc = event['ResourceProperties']['Vpc'] input_count = event['ResourceProperties']['Input'] print("Processing subnets: %s." % input_subnet_list) ec2 = boto3.client('ec2') if len(input_subnet_list) != int(input_count): error_and_exit("The amount of seleced subnets and the 'SelectedPrivateSubnetsCont' value differs.") print("Validating subnets and VPC ...") response = ec2.describe_subnets(Filters=[ { 'Name': 'vpc-id', 'Values': [ the_vpc, ] }, ]) vpc_subnets = list(map(lambda subnet: subnet['SubnetId'], response['Subnets'])) if not all(subnet in vpc_subnets for subnet in input_subnet_list): error_and_exit("All the subnets must belong to the " + "same VPC [%s]." % the_vpc) print("[OK] Passed!") print("Validating subnets and AZs ...") az_s = [] for sub in response['Subnets']: if sub['SubnetId'] in input_subnet_list: if sub['AvailabilityZone'] not in az_s: az_s.append(sub['AvailabilityZone']) else: error_and_exit("Only '1' Subnet per Availability Zone is allowed. You have at least (2) in AZ [%s]." % sub['AvailabilityZone']) print("[OK] Passed!") print("Validating that subnets are private ...") response = ec2.describe_route_tables( Filters=[ { 'Name': 'association.subnet-id', 'Values': input_subnet_list }, ]) route_table_list = list(map(lambda route: route['Routes'], response['RouteTables'])) for route in [r for sub_list in route_table_list for r in sub_list]: gw_id = route.get('GatewayId', None) if gw_id and gw_id.startswith('igw-'): error_and_exit("This setup does not allow public subnets. Please select only private subnets.") print("[OK] Passed!") print("Getting VPC CIDR ...") response_data['vpc_cidr'] = ec2.describe_vpcs(Filters=[ { 'Name': 'vpc-id', 'Values': [ the_vpc, ] }, ])['Vpcs'][0]['CidrBlock'] print("CIDR is: %s" % response_data['vpc_cidr']) print("Execution successful!") cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data, physicalResourceId=phy_res_id) except Exception as e: print("Execution failed...") e_string = str(e) print(e_string) context.log_stream_name = e_string cfnresponse.send(event, context, cfnresponse.FAILED, response_data, physicalResourceId=phy_res_id) SubnetCheckerFunctionExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Version: '2012-10-17' Path: "/" Policies: - PolicyDocument: Statement: - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: arn:aws:logs:*:*:* Version: '2012-10-17' PolicyName: !Sub ${AWS::StackName}-SubetChecker-sub-CW - PolicyDocument: Statement: - Action: - ec2:DescribeVpcs - ec2:DescribeSubnets - ec2:DescribeRouteTables Effect: Allow Resource: - '*' Version: '2012-10-17' PolicyName: !Sub ${AWS::StackName}-SubetChecker-sub ### ## Id Generator Function # IdGeneratorFunction: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: !Ref LambdaRuntime Timeout: 360 Role: !GetAtt IdGeneratorFunctionExecutionRole.Arn InlineCode: | import boto3 import cfnresponse def handler(event, context): try: the_event = event['RequestType'] response_data = {} phy_res_id = str(hash(event['StackId'] + event['LogicalResourceId']))[1:] print("Event dump: %s" % str(event)) print("Request type: ", str(the_event)) if the_event in ('Create', 'Update'): unique_id = str(hash(event['StackId']))[1:11] print("Unique ID: %s ." % unique_id) response_data['unique_id'] = unique_id print("Execution successful!") cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data, physicalResourceId=phy_res_id) except Exception as e: print("Execution failed...") e_string = str(e) print(e_string) context.log_stream_name = e_string cfnresponse.send(event, context, cfnresponse.FAILED, response_data, physicalResourceId=phy_res_id) IdGeneratorFunctionExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Version: '2012-10-17' Path: "/" Policies: - PolicyDocument: Statement: - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: arn:aws:logs:*:*:* Version: '2012-10-17' PolicyName: !Sub ${AWS::StackName}-IdGenerator-sub-CW Outputs: SubnetChecker: Description: 'Subnet Checker Service Token.' Value: !GetAtt SubnetCheckerFunction.Arn IdGenerator: Description: 'ID generator Service Token.' Value: !GetAtt IdGeneratorFunction.Arn