AWSTemplateFormatVersion: "2010-09-09" Description: Production, and non-production infrastructure Parameters: LatestAmiId: Type: "AWS::SSM::Parameter::Value" Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" CoreNetworkId: Type: String Default: "" CoreNetworkArn: Type: String Default: "" CreateCWANAttachment: Type: String Description: Create the Cloud WAN VPC attachments Default: "false" AllowedValues: [true, false] CreateVPCRoutes: Type: String Description: Create VPC routes to Core Network attachments Default: "false" AllowedValues: [true, false] Conditions: CWANValuesAdded: !And - !Not [!Equals [!Ref CoreNetworkId, ""]] - !Not [!Equals [!Ref CoreNetworkArn, ""]] CreateAttachment: !And - !Equals [!Ref CreateCWANAttachment, 'true'] - !Condition CWANValuesAdded CreateRoutes: !And - !Condition CWANValuesAdded - !Equals [!Ref CreateCWANAttachment, 'true'] - !Equals [!Ref CreateVPCRoutes, 'true'] Mappings: RegionMap: us-west-2: prod: 10.0.0.0/16 nonprod: 10.1.0.0/16 eu-north-1: prod: 10.10.0.0/16 nonprod: 10.11.0.0/16 Resources: # ---------- PRODUCTION VPC ---------- # VPC resource VPCProd: Metadata: cfn_nag: rules_to_suppress: - id: W60 reason: VPC Flow Logs not required for workshop Type: AWS::EC2::VPC Properties: CidrBlock: !FindInMap - RegionMap - !Ref "AWS::Region" - prod EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "vpc" # Subnets VPCProdSubnetWorkload1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCProd CidrBlock: !Select [0, !Cidr [!GetAtt VPCProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 0 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "workload-subnet-1" VPCProdSubnetWorkload2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCProd CidrBlock: !Select [1, !Cidr [!GetAtt VPCProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 1 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "workload-subnet-2" VPCProdSubnetEndpoints1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCProd CidrBlock: !Select [2, !Cidr [!GetAtt VPCProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 0 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "endpoints-subnet-1" VPCProdSubnetEndpoints2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCProd CidrBlock: !Select [3, !Cidr [!GetAtt VPCProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 1 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "endpoints-subnet-2" VPCProdSubnetCWAN1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCProd CidrBlock: !Select [4, !Cidr [!GetAtt VPCProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 0 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "cwan-subnet-1" VPCProdSubnetCWAN2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCProd CidrBlock: !Select [5, !Cidr [!GetAtt VPCProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 1 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "cwan-subnet-2" # Route Tables VPCProdRouteTableWorkload1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCProd Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "workload-rt-1" VPCProdWorkloadSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCProdRouteTableWorkload1 SubnetId: !Ref VPCProdSubnetWorkload1 VPCProdRouteTableWorkload2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCProd Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "workload-rt-2" VPCProdWorkloadSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCProdRouteTableWorkload2 SubnetId: !Ref VPCProdSubnetWorkload2 VPCProdRouteTableEndpoints1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCProd Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "endpoints-rt-1" VPCProdEndpointsSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCProdRouteTableEndpoints1 SubnetId: !Ref VPCProdSubnetEndpoints1 VPCProdRouteTableEndpoints2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCProd Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "endpoints-rt-2" VPCProdEndpointsSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCProdRouteTableEndpoints2 SubnetId: !Ref VPCProdSubnetEndpoints2 VPCProdRouteTableCWAN1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCProd Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "cwan-rt-1" VPCProdCWANSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCProdRouteTableCWAN1 SubnetId: !Ref VPCProdSubnetCWAN1 VPCProdRouteTableCWAN2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCProd Tags: - Key: Name Value: !Join - "-" - - "prod" - !Ref AWS::Region - "cwan-rt-2" VPCProdTGWSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCProdRouteTableCWAN2 SubnetId: !Ref VPCProdSubnetCWAN2 # Security Groups (Instance and VPC endpoint) VPCProdInstanceSecurityGroup: Metadata: cfn_nag: rules_to_suppress: - id: W9 reason: CIDR is constrained to CloudWAN demo range Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Production VPC - Instance Security Group VpcId: !Ref VPCProd SecurityGroupIngress: - Description: Allowing ICMP traffic IpProtocol: icmp FromPort: -1 ToPort: -1 CidrIp: 10.0.0.0/8 VPCProdEndpointsSecurityGroup: Metadata: cfn_nag: rules_to_suppress: - id: W9 reason: CIDR is constrained to CloudWAN demo range Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Production VPC - Endpoints Security Group VpcId: !Ref VPCProd SecurityGroupIngress: - Description: Allowing HTTPS IpProtocol: tcp FromPort: 443 ToPort: 443 SourceSecurityGroupId: !Ref VPCProdInstanceSecurityGroup # EC2 Instances EC2InstanceProdWorkload1: Type: AWS::EC2::Instance Properties: InstanceType: t3.micro SecurityGroupIds: - !Ref VPCProdInstanceSecurityGroup SubnetId: !Ref VPCProdSubnetWorkload1 ImageId: !Ref LatestAmiId IamInstanceProfile: !Ref EC2SSMInstanceProfileWorkloads Tags: - Key: Name Value: "Prod-Instance-1" EC2InstanceProdWorkload2: Type: AWS::EC2::Instance Properties: InstanceType: t3.micro SecurityGroupIds: - !Ref VPCProdInstanceSecurityGroup SubnetId: !Ref VPCProdSubnetWorkload2 ImageId: !Ref LatestAmiId IamInstanceProfile: !Ref EC2SSMInstanceProfileWorkloads Tags: - Key: Name Value: "Prod-Instance-2" # SSM Endpoints SSMProdVPCEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm VpcId: !Ref VPCProd SubnetIds: - !Ref VPCProdSubnetEndpoints1 - !Ref VPCProdSubnetEndpoints2 SecurityGroupIds: - !Ref VPCProdEndpointsSecurityGroup VpcEndpointType: Interface PrivateDnsEnabled: True SSMMessagesProdVPCEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages VpcId: !Ref VPCProd SubnetIds: - !Ref VPCProdSubnetEndpoints1 - !Ref VPCProdSubnetEndpoints2 SecurityGroupIds: - !Ref VPCProdEndpointsSecurityGroup VpcEndpointType: Interface PrivateDnsEnabled: True EC2MessagesProdVPCEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages VpcId: !Ref VPCProd SubnetIds: - !Ref VPCProdSubnetEndpoints1 - !Ref VPCProdSubnetEndpoints2 SecurityGroupIds: - !Ref VPCProdEndpointsSecurityGroup VpcEndpointType: Interface PrivateDnsEnabled: True # ---------- NON-PRODUCTION VPC ---------- # VPC resource VPCNonProd: Metadata: cfn_nag: rules_to_suppress: - id: W60 reason: VPC Flow Logs not required for workshop Type: AWS::EC2::VPC Properties: CidrBlock: !FindInMap - RegionMap - !Ref "AWS::Region" - nonprod EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "vpc" # Subnets VPCNonProdSubnetWorkload1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCNonProd CidrBlock: !Select [0, !Cidr [!GetAtt VPCNonProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 0 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "workload-subnet-1" VPCNonProdSubnetWorkload2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCNonProd CidrBlock: !Select [1, !Cidr [!GetAtt VPCNonProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 1 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "workload-subnet-2" VPCNonProdSubnetEndpoints1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCNonProd CidrBlock: !Select [2, !Cidr [!GetAtt VPCNonProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 0 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "endpoints-subnet-1" VPCNonProdSubnetEndpoints2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCNonProd CidrBlock: !Select [3, !Cidr [!GetAtt VPCNonProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 1 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "endpoints-subnet-2" VPCNonProdSubnetCWAN1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCNonProd CidrBlock: !Select [4, !Cidr [!GetAtt VPCNonProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 0 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "cwan-subnet-1" VPCNonProdSubnetCWAN2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPCNonProd CidrBlock: !Select [5, !Cidr [!GetAtt VPCNonProd.CidrBlock, 6, 8]] AvailabilityZone: !Select - 1 - !GetAZs Ref: "AWS::Region" MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "cwan-subnet-2" # Route Tables VPCNonProdRouteTableWorkload1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCNonProd Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "workload-rt-1" VPCNonProdWorkloadSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCNonProdRouteTableWorkload1 SubnetId: !Ref VPCNonProdSubnetWorkload1 VPCNonProdRouteTableWorkload2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCNonProd Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "workload-rt-2" VPCNonProdWorkloadSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCNonProdRouteTableWorkload2 SubnetId: !Ref VPCNonProdSubnetWorkload2 VPCNonProdRouteTableEndpoints1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCNonProd Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "endpoints-rt-1" VPCNonProdEndpointsSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCNonProdRouteTableEndpoints1 SubnetId: !Ref VPCNonProdSubnetEndpoints1 VPCNonProdRouteTableEndpoints2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCNonProd Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "endpoints-rt-2" VPCNonProdEndpointsSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCNonProdRouteTableEndpoints2 SubnetId: !Ref VPCNonProdSubnetEndpoints2 VPCNonProdRouteTableCWAN1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCNonProd Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "cwan-rt-1" VPCNonProdCWANSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCNonProdRouteTableCWAN1 SubnetId: !Ref VPCNonProdSubnetCWAN1 VPCNonProdRouteTableCWAN2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPCNonProd Tags: - Key: Name Value: !Join - "-" - - "nonprod" - !Ref AWS::Region - "cwan-rt-2" VPCNonProdTGWSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref VPCNonProdRouteTableCWAN2 SubnetId: !Ref VPCNonProdSubnetCWAN2 # Security Groups (Instance and VPC endpoint) VPCNonProdInstanceSecurityGroup: Metadata: cfn_nag: rules_to_suppress: - id: W9 reason: CIDR is constrained to CloudWAN demo range Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Non-Production VPC - Instance Security Group VpcId: !Ref VPCNonProd SecurityGroupIngress: - Description: Allowing ICMP traffic IpProtocol: icmp FromPort: "-1" ToPort: "-1" CidrIp: 10.0.0.0/8 VPCNonProdEndpointsSecurityGroup: Metadata: cfn_nag: rules_to_suppress: - id: W9 reason: CIDR is constrained to CloudWAN demo range Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Non-Production VPC - VPC Endpoints Security Group VpcId: !Ref VPCNonProd SecurityGroupIngress: - Description: Allowing HTTPS IpProtocol: tcp FromPort: 443 ToPort: 443 SourceSecurityGroupId: !Ref VPCNonProdInstanceSecurityGroup # EC2 Instances EC2InstanceNonProdWorkload1: Type: AWS::EC2::Instance Properties: InstanceType: t3.micro SecurityGroupIds: - Ref: VPCNonProdInstanceSecurityGroup SubnetId: Ref: VPCNonProdSubnetWorkload1 ImageId: !Ref LatestAmiId IamInstanceProfile: !Ref EC2SSMInstanceProfileWorkloads Tags: - Key: Name Value: "NonProd-Instance-1" EC2InstanceNonProdWorkload2: Type: AWS::EC2::Instance Properties: InstanceType: t3.micro SecurityGroupIds: - Ref: VPCNonProdInstanceSecurityGroup SubnetId: Ref: VPCNonProdSubnetWorkload2 ImageId: !Ref LatestAmiId IamInstanceProfile: !Ref EC2SSMInstanceProfileWorkloads Tags: - Key: Name Value: "NonProd-Instance-2" # SSM Endpoints SSMNonProdVPCEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm VpcId: !Ref VPCNonProd SubnetIds: - !Ref VPCNonProdSubnetEndpoints1 - !Ref VPCNonProdSubnetEndpoints2 SecurityGroupIds: - !Ref VPCNonProdEndpointsSecurityGroup VpcEndpointType: Interface PrivateDnsEnabled: True SSMMessagesNonProdVPCEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages VpcId: !Ref VPCNonProd SubnetIds: - !Ref VPCNonProdSubnetEndpoints1 - !Ref VPCNonProdSubnetEndpoints2 SecurityGroupIds: - !Ref VPCNonProdEndpointsSecurityGroup VpcEndpointType: Interface PrivateDnsEnabled: True EC2MessagesNonProdVPCEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages VpcId: !Ref VPCNonProd SubnetIds: - !Ref VPCNonProdSubnetEndpoints1 - !Ref VPCNonProdSubnetEndpoints2 SecurityGroupIds: - !Ref VPCNonProdEndpointsSecurityGroup VpcEndpointType: Interface PrivateDnsEnabled: True # ---------- IAM ROLE (EC2 INSTANCE) ---------- # EC2 Instance Role (SSM access) EC2SSMIAMRoleWorkloads: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore Path: / EC2SSMInstanceProfileWorkloads: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !Ref EC2SSMIAMRoleWorkloads # ---------- CLOUD WAN ATTACHMENTS ---------- # Production VPC ProdCWANAttachment: Condition: CreateAttachment Type: AWS::NetworkManager::VpcAttachment Properties: CoreNetworkId: !Ref CoreNetworkId SubnetArns: - Fn::Join: - "" - - "arn:aws:ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":subnet/" - Ref: VPCProdSubnetCWAN1 - Fn::Join: - "" - - "arn:aws:ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":subnet/" - Ref: VPCProdSubnetCWAN1 Tags: - Key: prod Value: true VpcArn: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":vpc/" - Ref: VPCProd # Non-Production VPC NonProdCWANAttachment: Condition: CreateAttachment Type: AWS::NetworkManager::VpcAttachment Properties: CoreNetworkId: Ref: CoreNetworkId SubnetArns: - Fn::Join: - "" - - "arn:aws:ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":subnet/" - Ref: VPCNonProdSubnetCWAN1 - Fn::Join: - "" - - "arn:aws:ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":subnet/" - Ref: VPCNonProdSubnetCWAN1 Tags: - Key: nonprod Value: true VpcArn: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":ec2:" - Ref: AWS::Region - ":" - Ref: AWS::AccountId - ":vpc/" - Ref: VPCNonProd # ---------- VPC ROUTES TO CLOUD WAN (CUSTOM RESOURCES) ---------- ProdRouteTableEntry1: Condition: CreateRoutes Type: Custom::RouteTableEntry DependsOn: - ProdCWANAttachment - FunctionLogGroup Properties: ServiceToken: !GetAtt RouteFunction.Arn Cidr: "0.0.0.0/0" RouteTableId: !Ref VPCProdRouteTableWorkload1 CoreNetworkArn: !Ref CoreNetworkArn ProdRouteTableEntry2: Condition: CreateRoutes Type: Custom::RouteTableEntry DependsOn: - ProdCWANAttachment - FunctionLogGroup Properties: ServiceToken: !GetAtt RouteFunction.Arn Cidr: "0.0.0.0/0" RouteTableId: !Ref VPCProdRouteTableWorkload2 CoreNetworkArn: !Ref CoreNetworkArn NonProdRouteTableEntry1: Condition: CreateRoutes Type: Custom::RouteTableEntry DependsOn: - NonProdCWANAttachment - FunctionLogGroup Properties: ServiceToken: !GetAtt RouteFunction.Arn Cidr: "0.0.0.0/0" RouteTableId: !Ref VPCNonProdRouteTableWorkload1 CoreNetworkArn: !Ref CoreNetworkArn NonProdRouteTableEntry2: Condition: CreateRoutes Type: Custom::RouteTableEntry DependsOn: - NonProdCWANAttachment - FunctionLogGroup Properties: ServiceToken: !GetAtt RouteFunction.Arn Cidr: "0.0.0.0/0" RouteTableId: !Ref VPCNonProdRouteTableWorkload2 CoreNetworkArn: !Ref CoreNetworkArn RouteFunctionRole: Condition: CreateRoutes Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: AllowLambdaVPC PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - ec2:CreateRoute - ec2:UpdateRoute - ec2:DeleteRoute Resource: - !Sub arn:${AWS::Partition}:ec2:*:*:route-table/* ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole FunctionLogGroup: Condition: CreateRoutes Metadata: cfn_nag: rules_to_suppress: - id: W84 reason: Encryption not required for this log group Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${RouteFunction} RetentionInDays: 7 RouteFunction: Condition: CreateRoutes Metadata: cfn_nag: rules_to_suppress: - id: W58 reason: CWL permissions granted by use of AWSLambdaBasicExecutionRole - id: W89 reason: No requirement for this function to be in a VPC - id: W92 reason: No requirement to limit simultaneous executions Type: AWS::Lambda::Function Properties: Description: Manage route table entry for CoreNetwork Runtime: python3.9 Timeout: 10 Role: !GetAtt RouteFunctionRole.Arn Handler: index.lambda_handler Code: ZipFile: |- import logging import boto3 import json import cfnresponse from botocore.exceptions import ClientError log = logging.getLogger("handler") log.setLevel(logging.INFO) def lambda_handler(event, context): try: log.info("Received event: %s", json.dumps(event)) action = event["RequestType"] cidr = event["ResourceProperties"]["Cidr"] table_id = event["ResourceProperties"]["RouteTableId"] core_arn = event["ResourceProperties"]["CoreNetworkArn"] ec2 = boto3.client("ec2") response = {} if action == "Create": log.info( "Creating route to %s with arn %s for table %s", cidr, core_arn, table_id, ) response = ec2.create_route( DestinationCidrBlock=cidr, RouteTableId=table_id, CoreNetworkArn=core_arn, ) if action == "Delete": log.info("Deleting route to %s in table %s", cidr, table_id) try: response = ec2.delete_route( DestinationCidrBlock=cidr, RouteTableId=table_id ) except ClientError as error: if error.response["Error"]["Code"] == "InvalidRoute.NotFound": response = {"Return": True} else: raise error if action == "Update": old_cidr = event["OldResourceProperties"]["Cidr"] old_table_id = event["OldResourceProperties"]["RouteTableId"] if old_cidr == cidr and old_table_id == table_id: log.info( "Updating route table %s entry for %s to %s", table_id, cidr, core_arn, ) ec2.replace_route( DestinationCidrBlock=cidr, RouteTableId=table_id, CoreNetworkArn=core_arn, ) response["Return"] = True else: log.info( "Replacing route with interruption due to change in cidr and/or table id" ) try: response = ec2.delete_route( DestinationCidrBlock=old_cidr, RouteTableId=old_table_id ) except ClientError as error: if error.response["Error"]["Code"] != "InvalidRoute.NotFound": raise error log.info( "Creating replacement route %s to %s in table %s", cidr, core_arn, table_id, ) response = ec2.create_route( DestinationCidrBlock=cidr, RouteTableId=table_id, CoreNetworkArn=core_arn, ) if "Return" in response: if response["Return"]: cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) return cfnresponse.send( event, context, cfnresponse.FAILED, {}, reason="API request failed" ) return cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) except: log.exception("whoops") cfnresponse.send( event, context, cfnresponse.FAILED, {}, reason="Caught exception, check logs", )