Skip to content

Migrate Windows Workloads

Applications

  • CloudEndure

  • AWS Server Migration Server -> Create AMIS

Diagram

Lab - Trust Relationship between AWS Managed AD and On-Premise AD with FSX

Lab Guide

AWS Support Contact: Hans Moser - hansmose@amazon.ch

  • Hans Moser - Info About D: Drive

    • Regarding D: Drive: In our ‘source Fileserver’ we have the files on C:, hence the first export also include the directory paths to C:
    • But on FSX, our target, the data drive is D: So to make the import work, we’ve to change the paths, so that we target the correct folder paths on the destination (our FSX).

Cloudformation

Parameters

Key Value
ADServer1InstanceType m5.large
ADServer1NetBIOSName DC1
ADServer1PrivateIP 10.0.0.10
ASGInstanceType m5.large
ASGSize 2
AvailabilityZones us-west-2a,us-west-2b
DemoInstances No
DeployManagedAD Yes
DomainAdminPassword ****
DomainAdminUser Admin
DomainDNSName example.com
DomainNetBIOSName example
ManagedADEdition Standard
ManagedDomainDNSName managedexample.com
ManagedDomainNetBIOSName managedexample
NumberOfAZs 2
PrivateSubnet1CIDR 10.0.0.0/19
PrivateSubnet2CIDR 10.0.32.0/19
PrivateSubnet3CIDR -
PublicSubnet1CIDR 10.0.128.0/20
PublicSubnet2CIDR 10.0.144.0/20
PublicSubnet3CIDR -
QSS3BucketName alpublic
QSS3KeyPrefix quickstart-microsoft-activedirectory/
SrvInstanceType t3.large
VPCCIDR 10.0.0.0/16

VPCStack

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "This template creates a Multi-AZ, multi-subnet VPC infrastructure with managed NAT gateways in the public subnet for each Availability Zone. You can also create additional private subnets with dedicated custom network access control lists (ACLs). If you deploy the Quick Start in a region that doesn't support NAT gateways, NAT instances are deployed instead. **WARNING** This template creates AWS resources. You will be billed for the AWS resources used if you create a stack from this template. QS(0027)",
    "Metadata": {
        "AWS::CloudFormation::Interface": {
            "ParameterGroups": [
                {
                    "Label": {
                        "default": "Availability Zone Configuration"
                    },
                    "Parameters": [
                        "AvailabilityZones",
                        "NumberOfAZs"
                    ]
                },
                {
                    "Label": {
                        "default": "Network Configuration"
                    },
                    "Parameters": [
                        "VPCCIDR",
                        "PublicSubnet1CIDR",
                        "PublicSubnet2CIDR",
                        "PublicSubnet3CIDR",
                        "PublicSubnet4CIDR",
                        "PublicSubnetTag1",
                        "PublicSubnetTag2",
                        "PublicSubnetTag3",
                        "CreatePrivateSubnets",
                        "PrivateSubnet1ACIDR",
                        "PrivateSubnet2ACIDR",
                        "PrivateSubnet3ACIDR",
                        "PrivateSubnet4ACIDR",
                        "PrivateSubnetATag1",
                        "PrivateSubnetATag2",
                        "PrivateSubnetATag3",
                        "CreateAdditionalPrivateSubnets",
                        "PrivateSubnet1BCIDR",
                        "PrivateSubnet2BCIDR",
                        "PrivateSubnet3BCIDR",
                        "PrivateSubnet4BCIDR",
                        "PrivateSubnetBTag1",
                        "PrivateSubnetBTag2",
                        "PrivateSubnetBTag3",
                        "VPCTenancy"
                    ]
                },
                {
                    "Label": {
                        "default": "Deprecated: NAT Instance Configuration"
                    },
                    "Parameters": [
                        "KeyPairName",
                        "NATInstanceType"
                    ]
                }
            ],
            "ParameterLabels": {
                "AvailabilityZones": {
                    "default": "Availability Zones"
                },
                "CreateAdditionalPrivateSubnets": {
                    "default": "Create additional private subnets with dedicated network ACLs"
                },
                "CreatePrivateSubnets": {
                    "default": "Create private subnets"
                },
                "KeyPairName": {
                    "default": "Deprecated: Key pair name"
                },
                "NATInstanceType": {
                    "default": "Deprecated: NAT instance type"
                },
                "NumberOfAZs": {
                    "default": "Number of Availability Zones"
                },
                "PrivateSubnet1ACIDR": {
                    "default": "Private subnet 1A CIDR"
                },
                "PrivateSubnet1BCIDR": {
                    "default": "Private subnet 1B with dedicated network ACL CIDR"
                },
                "PrivateSubnet2ACIDR": {
                    "default": "Private subnet 2A CIDR"
                },
                "PrivateSubnet2BCIDR": {
                    "default": "Private subnet 2B with dedicated network ACL CIDR"
                },
                "PrivateSubnet3ACIDR": {
                    "default": "Private subnet 3A CIDR"
                },
                "PrivateSubnet3BCIDR": {
                    "default": "Private subnet 3B with dedicated network ACL CIDR"
                },
                "PrivateSubnet4ACIDR": {
                    "default": "Private subnet 4A CIDR"
                },
                "PrivateSubnet4BCIDR": {
                    "default": "Private subnet 4B with dedicated network ACL CIDR"
                },
                "PrivateSubnetATag1": {
                    "default": "Tag for Private A Subnets"
                },
                "PrivateSubnetATag2": {
                    "default": "Tag for Private A Subnets"
                },
                "PrivateSubnetATag3": {
                    "default": "Tag for Private A Subnets"
                },
                "PrivateSubnetBTag1": {
                    "default": "Tag for Private B Subnets"
                },
                "PrivateSubnetBTag2": {
                    "default": "Tag for Private B Subnets"
                },
                "PrivateSubnetBTag3": {
                    "default": "Tag for Private B Subnets"
                },
                "PublicSubnet1CIDR": {
                    "default": "Public subnet 1 CIDR"
                },
                "PublicSubnet2CIDR": {
                    "default": "Public subnet 2 CIDR"
                },
                "PublicSubnet3CIDR": {
                    "default": "Public subnet 3 CIDR"
                },
                "PublicSubnet4CIDR": {
                    "default": "Public subnet 4 CIDR"
                },
                "PublicSubnetTag1": {
                    "default": "Tag for Public Subnets"
                },
                "PublicSubnetTag2": {
                    "default": "Tag for Public Subnets"
                },
                "PublicSubnetTag3": {
                    "default": "Tag for Public Subnets"
                },
                "VPCCIDR": {
                    "default": "VPC CIDR"
                },
                "VPCTenancy": {
                    "default": "VPC Tenancy"
                }
            }
        }
    },
    "Parameters": {
        "AvailabilityZones": {
            "Description": "List of Availability Zones to use for the subnets in the VPC. Note: The logical order is preserved.",
            "Type": "List<AWS::EC2::AvailabilityZone::Name>"
        },
        "CreateAdditionalPrivateSubnets": {
            "AllowedValues": [
                "true",
                "false"
            ],
            "Default": "false",
            "Description": "Set to true to create a network ACL protected subnet in each Availability Zone. If false, the CIDR parameters for those subnets will be ignored. If true, it also requires that the 'Create private subnets' parameter is also true to have any effect.",
            "Type": "String"
        },
        "CreatePrivateSubnets": {
            "AllowedValues": [
                "true",
                "false"
            ],
            "Default": "true",
            "Description": "Set to false to create only public subnets. If false, the CIDR parameters for ALL private subnets will be ignored.",
            "Type": "String"
        },
        "KeyPairName": {
            "Description": "Deprecated. NAT gateways are now supported in all regions.",
            "Type": "String",
            "Default": "deprecated"
        },
        "NATInstanceType": {
            "Default": "deprecated",
            "Description": "Deprecated. NAT gateways are now supported in all regions.",
            "Type": "String"
        },
        "NumberOfAZs": {
            "AllowedValues": [
                "2",
                "3",
                "4"
            ],
            "Default": "2",
            "Description": "Number of Availability Zones to use in the VPC. This must match your selections in the list of Availability Zones parameter.",
            "Type": "String"
        },
        "PrivateSubnet1ACIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.0.0/19",
            "Description": "CIDR block for private subnet 1A located in Availability Zone 1",
            "Type": "String"
        },
        "PrivateSubnet1BCIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.192.0/21",
            "Description": "CIDR block for private subnet 1B with dedicated network ACL located in Availability Zone 1",
            "Type": "String"
        },
        "PrivateSubnet2ACIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.32.0/19",
            "Description": "CIDR block for private subnet 2A located in Availability Zone 2",
            "Type": "String"
        },
        "PrivateSubnet2BCIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.200.0/21",
            "Description": "CIDR block for private subnet 2B with dedicated network ACL located in Availability Zone 2",
            "Type": "String"
        },
        "PrivateSubnet3ACIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.64.0/19",
            "Description": "CIDR block for private subnet 3A located in Availability Zone 3",
            "Type": "String"
        },
        "PrivateSubnet3BCIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.208.0/21",
            "Description": "CIDR block for private subnet 3B with dedicated network ACL located in Availability Zone 3",
            "Type": "String"
        },
        "PrivateSubnet4ACIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.96.0/19",
            "Description": "CIDR block for private subnet 4A located in Availability Zone 4",
            "Type": "String"
        },
        "PrivateSubnet4BCIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.216.0/21",
            "Description": "CIDR block for private subnet 4B with dedicated network ACL located in Availability Zone 4",
            "Type": "String"
        },
        "PrivateSubnetATag1": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "Network=Private",
            "Description": "tag to add to private subnets A, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PrivateSubnetATag2": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "",
            "Description": "tag to add to private subnets A, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PrivateSubnetATag3": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "",
            "Description": "tag to add to private subnets A, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PrivateSubnetBTag1": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "Network=Private",
            "Description": "tag to add to private subnets B, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PrivateSubnetBTag2": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "",
            "Description": "tag to add to private subnets B, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PrivateSubnetBTag3": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "",
            "Description": "tag to add to private subnets B, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PublicSubnet1CIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.128.0/20",
            "Description": "CIDR block for the public DMZ subnet 1 located in Availability Zone 1",
            "Type": "String"
        },
        "PublicSubnet2CIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.144.0/20",
            "Description": "CIDR block for the public DMZ subnet 2 located in Availability Zone 2",
            "Type": "String"
        },
        "PublicSubnet3CIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.160.0/20",
            "Description": "CIDR block for the public DMZ subnet 3 located in Availability Zone 3",
            "Type": "String"
        },
        "PublicSubnet4CIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.176.0/20",
            "Description": "CIDR block for the public DMZ subnet 4 located in Availability Zone 4",
            "Type": "String"
        },
        "PublicSubnetTag1": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "Network=Public",
            "Description": "tag to add to public subnets, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PublicSubnetTag2": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "",
            "Description": "tag to add to public subnets, in format Key=Value (Optional)",
            "Type": "String"
        },
        "PublicSubnetTag3": {
            "AllowedPattern": "^([a-zA-Z0-9+\\-._:/@]+=[a-zA-Z0-9+\\-.,_:/@ *\\\\\"'\\[\\]\\{\\}]*)?$",
            "ConstraintDescription": "tags must be in format \"Key=Value\" keys can only contain [a-zA-Z0-9+\\-._:/@], values can contain [a-zA-Z0-9+\\-._:/@ *\\\\\"'\\[\\]\\{\\}]",
            "Default": "",
            "Description": "tag to add to public subnets, in format Key=Value (Optional)",
            "Type": "String"
        },
        "VPCCIDR": {
            "AllowedPattern": "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$",
            "ConstraintDescription": "CIDR block parameter must be in the form x.x.x.x/16-28",
            "Default": "10.0.0.0/16",
            "Description": "CIDR block for the VPC",
            "Type": "String"
        },
        "VPCTenancy": {
            "AllowedValues": [
                "default",
                "dedicated"
            ],
            "Default": "default",
            "Description": "The allowed tenancy of instances launched into the VPC",
            "Type": "String"
        }
    },
    "Conditions": {
        "3AZCondition": {
            "Fn::Or": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "NumberOfAZs"
                        },
                        "3"
                    ]
                },
                {
                    "Condition": "4AZCondition"
                }
            ]
        },
        "4AZCondition": {
            "Fn::Equals": [
                {
                    "Ref": "NumberOfAZs"
                },
                "4"
            ]
        },
        "AdditionalPrivateSubnetsCondition": {
            "Fn::And": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "CreatePrivateSubnets"
                        },
                        "true"
                    ]
                },
                {
                    "Fn::Equals": [
                        {
                            "Ref": "CreateAdditionalPrivateSubnets"
                        },
                        "true"
                    ]
                }
            ]
        },
        "AdditionalPrivateSubnets&3AZCondition": {
            "Fn::And": [
                {
                    "Condition": "AdditionalPrivateSubnetsCondition"
                },
                {
                    "Condition": "3AZCondition"
                }
            ]
        },
        "AdditionalPrivateSubnets&4AZCondition": {
            "Fn::And": [
                {
                    "Condition": "AdditionalPrivateSubnetsCondition"
                },
                {
                    "Condition": "4AZCondition"
                }
            ]
        },
        "GovCloudCondition": {
            "Fn::Equals": [
                {
                    "Ref": "AWS::Region"
                },
                "us-gov-west-1"
            ]
        },
        "NVirginiaRegionCondition": {
            "Fn::Equals": [
                {
                    "Ref": "AWS::Region"
                },
                "us-east-1"
            ]
        },
        "PrivateSubnetsCondition": {
            "Fn::Equals": [
                {
                    "Ref": "CreatePrivateSubnets"
                },
                "true"
            ]
        },
        "PrivateSubnets&3AZCondition": {
            "Fn::And": [
                {
                    "Condition": "PrivateSubnetsCondition"
                },
                {
                    "Condition": "3AZCondition"
                }
            ]
        },
        "PrivateSubnets&4AZCondition": {
            "Fn::And": [
                {
                    "Condition": "PrivateSubnetsCondition"
                },
                {
                    "Condition": "4AZCondition"
                }
            ]
        },
        "PrivateSubnetATag1Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PrivateSubnetATag1"
                        },
                        ""
                    ]
                }
            ]
        },
        "PrivateSubnetATag2Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PrivateSubnetATag2"
                        },
                        ""
                    ]
                }
            ]
        },
        "PrivateSubnetATag3Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PrivateSubnetATag3"
                        },
                        ""
                    ]
                }
            ]
        },
        "PrivateSubnetBTag1Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PrivateSubnetBTag1"
                        },
                        ""
                    ]
                }
            ]
        },
        "PrivateSubnetBTag2Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PrivateSubnetBTag2"
                        },
                        ""
                    ]
                }
            ]
        },
        "PrivateSubnetBTag3Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PrivateSubnetBTag3"
                        },
                        ""
                    ]
                }
            ]
        },
        "PublicSubnetTag1Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PublicSubnetTag1"
                        },
                        ""
                    ]
                }
            ]
        },
        "PublicSubnetTag2Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PublicSubnetTag2"
                        },
                        ""
                    ]
                }
            ]
        },
        "PublicSubnetTag3Condition": {
            "Fn::Not": [
                {
                    "Fn::Equals": [
                        {
                            "Ref": "PublicSubnetTag3"
                        },
                        ""
                    ]
                }
            ]
        }
    },
    "Resources": {
        "DHCPOptions": {
            "Type": "AWS::EC2::DHCPOptions",
            "Properties": {
                "DomainName": {
                    "Fn::If": [
                        "NVirginiaRegionCondition",
                        "ec2.internal",
                        {
                            "Fn::Sub": "${AWS::Region}.compute.internal"
                        }
                    ]
                },
                "DomainNameServers": [
                    "AmazonProvidedDNS"
                ]
            }
        },
        "VPC": {
            "Type": "AWS::EC2::VPC",
            "Properties": {
                "CidrBlock": {
                    "Ref": "VPCCIDR"
                },
                "InstanceTenancy": {
                    "Ref": "VPCTenancy"
                },
                "EnableDnsSupport": true,
                "EnableDnsHostnames": true,
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Ref": "AWS::StackName"
                        }
                    }
                ]
            }
        },
        "VPCDHCPOptionsAssociation": {
            "Type": "AWS::EC2::VPCDHCPOptionsAssociation",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "DhcpOptionsId": {
                    "Ref": "DHCPOptions"
                }
            }
        },
        "InternetGateway": {
            "Type": "AWS::EC2::InternetGateway",
            "Properties": {
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": {
                            "Ref": "AWS::StackName"
                        }
                    }
                ]
            }
        },
        "VPCGatewayAttachment": {
            "Type": "AWS::EC2::VPCGatewayAttachment",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "InternetGatewayId": {
                    "Ref": "InternetGateway"
                }
            }
        },
        "PrivateSubnet1A": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet1ACIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "0",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 1A"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet1B": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet1BCIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "0",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 1B"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet2A": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet2ACIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "1",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 2A"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet2B": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet2BCIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "1",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 2B"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet3A": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet3ACIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "2",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 3A"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet3B": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet3BCIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "2",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 3B"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet4A": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet4ACIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "3",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 4A"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetATag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetATag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PrivateSubnet4B": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PrivateSubnet4BCIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "3",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 4B"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnetBTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PrivateSubnetBTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ]
            }
        },
        "PublicSubnet1": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PublicSubnet1CIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "0",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Public subnet 1"
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ],
                "MapPublicIpOnLaunch": true
            }
        },
        "PublicSubnet2": {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PublicSubnet2CIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "1",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Public subnet 2"
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ],
                "MapPublicIpOnLaunch": true
            }
        },
        "PublicSubnet3": {
            "Condition": "3AZCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PublicSubnet3CIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "2",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Public subnet 3"
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ],
                "MapPublicIpOnLaunch": true
            }
        },
        "PublicSubnet4": {
            "Condition": "4AZCondition",
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "CidrBlock": {
                    "Ref": "PublicSubnet4CIDR"
                },
                "AvailabilityZone": {
                    "Fn::Select": [
                        "3",
                        {
                            "Ref": "AvailabilityZones"
                        }
                    ]
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Public subnet 4"
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag1Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag2Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag2"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PublicSubnetTag3Condition",
                            {
                                "Key": {
                                    "Fn::Select": [
                                        "0",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                },
                                "Value": {
                                    "Fn::Select": [
                                        "1",
                                        {
                                            "Fn::Split": [
                                                "=",
                                                {
                                                    "Ref": "PublicSubnetTag3"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ],
                "MapPublicIpOnLaunch": true
            }
        },
        "PrivateSubnet1ARouteTable": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 1A"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet1ARoute": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet1ARouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway1"
                }
            }
        },
        "PrivateSubnet1ARouteTableAssociation": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet1A"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet1ARouteTable"
                }
            }
        },
        "PrivateSubnet2ARouteTable": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 2A"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet2ARoute": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet2ARouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway2"
                }
            }
        },
        "PrivateSubnet2ARouteTableAssociation": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet2A"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet2ARouteTable"
                }
            }
        },
        "PrivateSubnet3ARouteTable": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 3A"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet3ARoute": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet3ARouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway3"
                }
            }
        },
        "PrivateSubnet3ARouteTableAssociation": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet3A"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet3ARouteTable"
                }
            }
        },
        "PrivateSubnet4ARouteTable": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 4A"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet4ARoute": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet4ARouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway4"
                }
            }
        },
        "PrivateSubnet4ARouteTableAssociation": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet4A"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet4ARouteTable"
                }
            }
        },
        "PrivateSubnet1BRouteTable": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 1B"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet1BRoute": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet1BRouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway1"
                }
            }
        },
        "PrivateSubnet1BRouteTableAssociation": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet1B"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet1BRouteTable"
                }
            }
        },
        "PrivateSubnet1BNetworkAcl": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "NACL Protected subnet 1"
                    },
                    {
                        "Key": "Network",
                        "Value": "NACL Protected"
                    }
                ]
            }
        },
        "PrivateSubnet1BNetworkAclEntryInbound": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": false,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet1BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet1BNetworkAclEntryOutbound": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": true,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet1BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet1BNetworkAclAssociation": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet1B"
                },
                "NetworkAclId": {
                    "Ref": "PrivateSubnet1BNetworkAcl"
                }
            }
        },
        "PrivateSubnet2BRouteTable": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 2B"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet2BRoute": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet2BRouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway2"
                }
            }
        },
        "PrivateSubnet2BRouteTableAssociation": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet2B"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet2BRouteTable"
                }
            }
        },
        "PrivateSubnet2BNetworkAcl": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "NACL Protected subnet 2"
                    },
                    {
                        "Key": "Network",
                        "Value": "NACL Protected"
                    }
                ]
            }
        },
        "PrivateSubnet2BNetworkAclEntryInbound": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": false,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet2BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet2BNetworkAclEntryOutbound": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": true,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet2BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet2BNetworkAclAssociation": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet2B"
                },
                "NetworkAclId": {
                    "Ref": "PrivateSubnet2BNetworkAcl"
                }
            }
        },
        "PrivateSubnet3BRouteTable": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 3B"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet3BRoute": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet3BRouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway3"
                }
            }
        },
        "PrivateSubnet3BRouteTableAssociation": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet3B"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet3BRouteTable"
                }
            }
        },
        "PrivateSubnet3BNetworkAcl": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "NACL Protected subnet 3"
                    },
                    {
                        "Key": "Network",
                        "Value": "NACL Protected"
                    }
                ]
            }
        },
        "PrivateSubnet3BNetworkAclEntryInbound": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": false,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet3BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet3BNetworkAclEntryOutbound": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": true,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet3BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet3BNetworkAclAssociation": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet3B"
                },
                "NetworkAclId": {
                    "Ref": "PrivateSubnet3BNetworkAcl"
                }
            }
        },
        "PrivateSubnet4BRouteTable": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Private subnet 4B"
                    },
                    {
                        "Key": "Network",
                        "Value": "Private"
                    }
                ]
            }
        },
        "PrivateSubnet4BRoute": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PrivateSubnet4BRouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "NatGatewayId": {
                    "Ref": "NATGateway4"
                }
            }
        },
        "PrivateSubnet4BRouteTableAssociation": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet4B"
                },
                "RouteTableId": {
                    "Ref": "PrivateSubnet4BRouteTable"
                }
            }
        },
        "PrivateSubnet4BNetworkAcl": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::NetworkAcl",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "NACL Protected subnet 4"
                    },
                    {
                        "Key": "Network",
                        "Value": "NACL Protected"
                    }
                ]
            }
        },
        "PrivateSubnet4BNetworkAclEntryInbound": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": false,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet4BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet4BNetworkAclEntryOutbound": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::NetworkAclEntry",
            "Properties": {
                "CidrBlock": "0.0.0.0/0",
                "Egress": true,
                "NetworkAclId": {
                    "Ref": "PrivateSubnet4BNetworkAcl"
                },
                "Protocol": -1,
                "RuleAction": "allow",
                "RuleNumber": 100
            }
        },
        "PrivateSubnet4BNetworkAclAssociation": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Type": "AWS::EC2::SubnetNetworkAclAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PrivateSubnet4B"
                },
                "NetworkAclId": {
                    "Ref": "PrivateSubnet4BNetworkAcl"
                }
            }
        },
        "PublicSubnetRouteTable": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": {
                    "Ref": "VPC"
                },
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "Public Subnets"
                    },
                    {
                        "Key": "Network",
                        "Value": "Public"
                    }
                ]
            }
        },
        "PublicSubnetRoute": {
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": {
                    "Ref": "PublicSubnetRouteTable"
                },
                "DestinationCidrBlock": "0.0.0.0/0",
                "GatewayId": {
                    "Ref": "InternetGateway"
                }
            }
        },
        "PublicSubnet1RouteTableAssociation": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PublicSubnet1"
                },
                "RouteTableId": {
                    "Ref": "PublicSubnetRouteTable"
                }
            }
        },
        "PublicSubnet2RouteTableAssociation": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PublicSubnet2"
                },
                "RouteTableId": {
                    "Ref": "PublicSubnetRouteTable"
                }
            }
        },
        "PublicSubnet3RouteTableAssociation": {
            "Condition": "3AZCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PublicSubnet3"
                },
                "RouteTableId": {
                    "Ref": "PublicSubnetRouteTable"
                }
            }
        },
        "PublicSubnet4RouteTableAssociation": {
            "Condition": "4AZCondition",
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": {
                    "Ref": "PublicSubnet4"
                },
                "RouteTableId": {
                    "Ref": "PublicSubnetRouteTable"
                }
            }
        },
        "NAT1EIP": {
            "Condition": "PrivateSubnetsCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::EIP",
            "Properties": {
                "Domain": "vpc"
            }
        },
        "NAT2EIP": {
            "Condition": "PrivateSubnetsCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::EIP",
            "Properties": {
                "Domain": "vpc"
            }
        },
        "NAT3EIP": {
            "Condition": "PrivateSubnets&3AZCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::EIP",
            "Properties": {
                "Domain": "vpc"
            }
        },
        "NAT4EIP": {
            "Condition": "PrivateSubnets&4AZCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::EIP",
            "Properties": {
                "Domain": "vpc"
            }
        },
        "NATGateway1": {
            "Condition": "PrivateSubnetsCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::NatGateway",
            "Properties": {
                "AllocationId": {
                    "Fn::GetAtt": [
                        "NAT1EIP",
                        "AllocationId"
                    ]
                },
                "SubnetId": {
                    "Ref": "PublicSubnet1"
                }
            }
        },
        "NATGateway2": {
            "Condition": "PrivateSubnetsCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::NatGateway",
            "Properties": {
                "AllocationId": {
                    "Fn::GetAtt": [
                        "NAT2EIP",
                        "AllocationId"
                    ]
                },
                "SubnetId": {
                    "Ref": "PublicSubnet2"
                }
            }
        },
        "NATGateway3": {
            "Condition": "PrivateSubnets&3AZCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::NatGateway",
            "Properties": {
                "AllocationId": {
                    "Fn::GetAtt": [
                        "NAT3EIP",
                        "AllocationId"
                    ]
                },
                "SubnetId": {
                    "Ref": "PublicSubnet3"
                }
            }
        },
        "NATGateway4": {
            "Condition": "PrivateSubnets&4AZCondition",
            "DependsOn": "VPCGatewayAttachment",
            "Type": "AWS::EC2::NatGateway",
            "Properties": {
                "AllocationId": {
                    "Fn::GetAtt": [
                        "NAT4EIP",
                        "AllocationId"
                    ]
                },
                "SubnetId": {
                    "Ref": "PublicSubnet4"
                }
            }
        },
        "S3VPCEndpoint": {
            "Condition": "PrivateSubnetsCondition",
            "Type": "AWS::EC2::VPCEndpoint",
            "Properties": {
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Action": "*",
                            "Effect": "Allow",
                            "Resource": "*",
                            "Principal": "*"
                        }
                    ]
                },
                "RouteTableIds": [
                    {
                        "Ref": "PrivateSubnet1ARouteTable"
                    },
                    {
                        "Ref": "PrivateSubnet2ARouteTable"
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnets&3AZCondition",
                            {
                                "Ref": "PrivateSubnet3ARouteTable"
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "PrivateSubnets&4AZCondition",
                            {
                                "Ref": "PrivateSubnet4ARouteTable"
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "AdditionalPrivateSubnetsCondition",
                            {
                                "Ref": "PrivateSubnet1BRouteTable"
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "AdditionalPrivateSubnetsCondition",
                            {
                                "Ref": "PrivateSubnet2BRouteTable"
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "AdditionalPrivateSubnets&3AZCondition",
                            {
                                "Ref": "PrivateSubnet3BRouteTable"
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    },
                    {
                        "Fn::If": [
                            "AdditionalPrivateSubnets&4AZCondition",
                            {
                                "Ref": "PrivateSubnet4BRouteTable"
                            },
                            {
                                "Ref": "AWS::NoValue"
                            }
                        ]
                    }
                ],
                "ServiceName": {
                    "Fn::Sub": "com.amazonaws.${AWS::Region}.s3"
                },
                "VpcId": {
                    "Ref": "VPC"
                }
            }
        }
    },
    "Outputs": {
        "NAT1EIP": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "NAT 1 IP address",
            "Value": {
                "Ref": "NAT1EIP"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-NAT1EIP"
                }
            }
        },
        "NAT2EIP": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "NAT 2 IP address",
            "Value": {
                "Ref": "NAT2EIP"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-NAT2EIP"
                }
            }
        },
        "NAT3EIP": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Description": "NAT 3 IP address",
            "Value": {
                "Ref": "NAT3EIP"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-NAT3EIP"
                }
            }
        },
        "NAT4EIP": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Description": "NAT 4 IP address",
            "Value": {
                "Ref": "NAT4EIP"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-NAT4EIP"
                }
            }
        },
        "PrivateSubnet1ACIDR": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "Private subnet 1A CIDR in Availability Zone 1",
            "Value": {
                "Ref": "PrivateSubnet1ACIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet1ACIDR"
                }
            }
        },
        "PrivateSubnet1AID": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "Private subnet 1A ID in Availability Zone 1",
            "Value": {
                "Ref": "PrivateSubnet1A"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet1AID"
                }
            }
        },
        "PrivateSubnet1BCIDR": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Description": "Private subnet 1B CIDR in Availability Zone 1",
            "Value": {
                "Ref": "PrivateSubnet1BCIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet1BCIDR"
                }
            }
        },
        "PrivateSubnet1BID": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Description": "Private subnet 1B ID in Availability Zone 1",
            "Value": {
                "Ref": "PrivateSubnet1B"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet1BID"
                }
            }
        },
        "PrivateSubnet2ACIDR": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "Private subnet 2A CIDR in Availability Zone 2",
            "Value": {
                "Ref": "PrivateSubnet2ACIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet2ACIDR"
                }
            }
        },
        "PrivateSubnet2AID": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "Private subnet 2A ID in Availability Zone 2",
            "Value": {
                "Ref": "PrivateSubnet2A"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet2AID"
                }
            }
        },
        "PrivateSubnet2BCIDR": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Description": "Private subnet 2B CIDR in Availability Zone 2",
            "Value": {
                "Ref": "PrivateSubnet2BCIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet2BCIDR"
                }
            }
        },
        "PrivateSubnet2BID": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Description": "Private subnet 2B ID in Availability Zone 2",
            "Value": {
                "Ref": "PrivateSubnet2B"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet2BID"
                }
            }
        },
        "PrivateSubnet3ACIDR": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Description": "Private subnet 3A CIDR in Availability Zone 3",
            "Value": {
                "Ref": "PrivateSubnet3ACIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet3ACIDR"
                }
            }
        },
        "PrivateSubnet3AID": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Description": "Private subnet 3A ID in Availability Zone 3",
            "Value": {
                "Ref": "PrivateSubnet3A"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet3AID"
                }
            }
        },
        "PrivateSubnet3BCIDR": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Description": "Private subnet 3B CIDR in Availability Zone 3",
            "Value": {
                "Ref": "PrivateSubnet3BCIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet3BCIDR"
                }
            }
        },
        "PrivateSubnet3BID": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Description": "Private subnet 3B ID in Availability Zone 3",
            "Value": {
                "Ref": "PrivateSubnet3B"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet3BID"
                }
            }
        },
        "PrivateSubnet4ACIDR": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Description": "Private subnet 4A CIDR in Availability Zone 4",
            "Value": {
                "Ref": "PrivateSubnet4ACIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet4ACIDR"
                }
            }
        },
        "PrivateSubnet4AID": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Description": "Private subnet 4A ID in Availability Zone 4",
            "Value": {
                "Ref": "PrivateSubnet4A"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet4AID"
                }
            }
        },
        "PrivateSubnet4BCIDR": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Description": "Private subnet 4B CIDR in Availability Zone 4",
            "Value": {
                "Ref": "PrivateSubnet4BCIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet4BCIDR"
                }
            }
        },
        "PrivateSubnet4BID": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Description": "Private subnet 4B ID in Availability Zone 4",
            "Value": {
                "Ref": "PrivateSubnet4B"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet4BID"
                }
            }
        },
        "PublicSubnet1CIDR": {
            "Description": "Public subnet 1 CIDR in Availability Zone 1",
            "Value": {
                "Ref": "PublicSubnet1CIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet1CIDR"
                }
            }
        },
        "PublicSubnet1ID": {
            "Description": "Public subnet 1 ID in Availability Zone 1",
            "Value": {
                "Ref": "PublicSubnet1"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet1ID"
                }
            }
        },
        "PublicSubnet2CIDR": {
            "Description": "Public subnet 2 CIDR in Availability Zone 2",
            "Value": {
                "Ref": "PublicSubnet2CIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet2CIDR"
                }
            }
        },
        "PublicSubnet2ID": {
            "Description": "Public subnet 2 ID in Availability Zone 2",
            "Value": {
                "Ref": "PublicSubnet2"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet2ID"
                }
            }
        },
        "PublicSubnet3CIDR": {
            "Condition": "3AZCondition",
            "Description": "Public subnet 3 CIDR in Availability Zone 3",
            "Value": {
                "Ref": "PublicSubnet3CIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet3CIDR"
                }
            }
        },
        "PublicSubnet3ID": {
            "Condition": "3AZCondition",
            "Description": "Public subnet 3 ID in Availability Zone 3",
            "Value": {
                "Ref": "PublicSubnet3"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet3ID"
                }
            }
        },
        "PublicSubnet4CIDR": {
            "Condition": "4AZCondition",
            "Description": "Public subnet 4 CIDR in Availability Zone 4",
            "Value": {
                "Ref": "PublicSubnet4CIDR"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet4CIDR"
                }
            }
        },
        "PublicSubnet4ID": {
            "Condition": "4AZCondition",
            "Description": "Public subnet 4 ID in Availability Zone 4",
            "Value": {
                "Ref": "PublicSubnet4"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnet4ID"
                }
            }
        },
        "S3VPCEndpoint": {
            "Condition": "PrivateSubnetsCondition",
            "Description": "S3 VPC Endpoint",
            "Value": {
                "Ref": "S3VPCEndpoint"
            },
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-S3VPCEndpoint"
                }
            }
        },
        "PrivateSubnet1ARouteTable": {
            "Condition": "PrivateSubnetsCondition",
            "Value": {
                "Ref": "PrivateSubnet1ARouteTable"
            },
            "Description": "Private subnet 1A route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet1ARouteTable"
                }
            }
        },
        "PrivateSubnet1BRouteTable": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Value": {
                "Ref": "PrivateSubnet1BRouteTable"
            },
            "Description": "Private subnet 1B route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet1BRouteTable"
                }
            }
        },
        "PrivateSubnet2ARouteTable": {
            "Condition": "PrivateSubnetsCondition",
            "Value": {
                "Ref": "PrivateSubnet2ARouteTable"
            },
            "Description": "Private subnet 2A route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet2ARouteTable"
                }
            }
        },
        "PrivateSubnet2BRouteTable": {
            "Condition": "AdditionalPrivateSubnetsCondition",
            "Value": {
                "Ref": "PrivateSubnet2BRouteTable"
            },
            "Description": "Private subnet 2B route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet2BRouteTable"
                }
            }
        },
        "PrivateSubnet3ARouteTable": {
            "Condition": "PrivateSubnets&3AZCondition",
            "Value": {
                "Ref": "PrivateSubnet3ARouteTable"
            },
            "Description": "Private subnet 3A route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet3ARouteTable"
                }
            }
        },
        "PrivateSubnet3BRouteTable": {
            "Condition": "AdditionalPrivateSubnets&3AZCondition",
            "Value": {
                "Ref": "PrivateSubnet3BRouteTable"
            },
            "Description": "Private subnet 3B route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet3BRouteTable"
                }
            }
        },
        "PrivateSubnet4ARouteTable": {
            "Condition": "PrivateSubnets&4AZCondition",
            "Value": {
                "Ref": "PrivateSubnet4ARouteTable"
            },
            "Description": "Private subnet 4A route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet4ARouteTable"
                }
            }
        },
        "PrivateSubnet4BRouteTable": {
            "Condition": "AdditionalPrivateSubnets&4AZCondition",
            "Value": {
                "Ref": "PrivateSubnet4BRouteTable"
            },
            "Description": "Private subnet 4B route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PrivateSubnet4BRouteTable"
                }
            }
        },
        "PublicSubnetRouteTable": {
            "Value": {
                "Ref": "PublicSubnetRouteTable"
            },
            "Description": "Public subnet route table",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-PublicSubnetRouteTable"
                }
            }
        },
        "VPCCIDR": {
            "Value": {
                "Ref": "VPCCIDR"
            },
            "Description": "VPC CIDR",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-VPCCIDR"
                }
            }
        },
        "VPCID": {
            "Value": {
                "Ref": "VPC"
            },
            "Description": "VPC ID",
            "Export": {
                "Name": {
                    "Fn::Sub": "${AWS::StackName}-VPCID"
                }
            }
        }
    }
}

ManagedADStack

AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  This template creates a managed Microsoft AD Directory Service into private subnets
  in separate Availability Zones inside a VPC. The default Domain Administrator user
  is 'admin'. For adding members to the domain, ensure that they are launched into
  the domain member security group created by this template and then configure them
  to use the AD instances fixed private IP addresses as the DNS server. **WARNING**
  This template creates Amazon EC2 Windows instance and related resources. You will
  be billed for the AWS resources used if you create a stack from this template. QS(0021)
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Network Configuration
        Parameters:
          - VPCCIDR
          - VPCID
          - PrivateSubnet1CIDR
          - PrivateSubnet1ID
          - PrivateSubnet2CIDR
          - PrivateSubnet2ID
          - PublicSubnet1CIDR
          - PublicSubnet2CIDR
      - Label:
          default: Microsoft Active Directory Configuration
        Parameters:
          - DomainDNSName
          - DomainNetBIOSName
          - DomainAdminPassword
          - ADEdition
      - Label:
          default: AWS Systems Manager AMI configuration
        Parameters:
          - WS2019FULLBASE
      - Label:
          default: AWS Quick Start Configuration
        Parameters:
          - QSS3BucketName
          - QSS3KeyPrefix
    ParameterLabels:
      DomainAdminPassword:
        default: Domain Admin Password
      DomainDNSName:
        default: Domain DNS Name
      DomainNetBIOSName:
        default: Domain NetBIOS Name
      ADEdition:
        default: AWS Microsoft AD edition
      PrivateSubnet1CIDR:
        default: Private Subnet 1 CIDR
      PrivateSubnet1ID:
        default: Private Subnet 1 ID
      PrivateSubnet2CIDR:
        default: Private Subnet 2 CIDR
      PrivateSubnet2ID:
        default: Private Subnet 2 ID
      PublicSubnet1CIDR:
        default: Public Subnet 1 CIDR
      PublicSubnet2CIDR:
        default: Public Subnet 2 CIDR
      QSS3BucketName:
        default: Quick Start S3 Bucket Name
      QSS3KeyPrefix:
        default: Quick Start S3 Key Prefix
      VPCCIDR:
        default: VPC CIDR
      VPCID:
        default: VPC ID
      WS2019FULLBASE: 
        default: Windows Server 2019 full base AMI
Parameters:
  DomainAdminPassword:
    Description: Password for the domain admin user. Must be at least 8 characters
      containing letters, numbers and symbols
    Type: String
    MinLength: '8'
    MaxLength: '32'
    AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*
    NoEcho: 'true'
  DomainDNSName:
    Description: Fully qualified domain name (FQDN) of the forest root domain e.g.
      example.com
    Type: String
    Default: example.com
    MinLength: '2'
    MaxLength: '255'
    AllowedPattern: '[a-zA-Z0-9\-]+\..+'
  DomainNetBIOSName:
    Description: NetBIOS name of the domain (upto 15 characters) for users of earlier
      versions of Windows e.g. EXAMPLE
    Type: String
    Default: example
    MinLength: '1'
    MaxLength: '15'
    AllowedPattern: '[a-zA-Z0-9\-]+'
  ADEdition:
    AllowedValues:
      - Standard
      - Enterprise
    Default: Enterprise
    Description: The AWS Microsoft AD edition. Valid values include Standard and Enterprise.
    Type: String
  PrivateSubnet1CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/19
    Description: CIDR block for private subnet 1 located in Availability Zone 1.
    Type: String
  PrivateSubnet1ID:
    Description: ID of the private subnet 1 in Availability Zone 1 (e.g., subnet-a0246dcd)
    Type: AWS::EC2::Subnet::Id
  PrivateSubnet2CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.32.0/19
    Description: CIDR block for private subnet 2 located in Availability Zone 2.
    Type: String
  PrivateSubnet2ID:
    Description: ID of the private subnet 2 in Availability Zone 2 (e.g., subnet-a0246dcd)
    Type: AWS::EC2::Subnet::Id
  PublicSubnet1CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.128.0/20
    Description: CIDR Block for the public DMZ subnet 1 located in Availability Zone
      1
    Type: String
  PublicSubnet2CIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.144.0/20
    Description: CIDR Block for the public DMZ subnet 2 located in Availability Zone
      2
    Type: String
  QSS3BucketName:
    AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$
    ConstraintDescription: Quick Start bucket name can include numbers, lowercase
      letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen
      (-).
    Default: aws-quickstart
    Description: S3 bucket name for the Quick Start assets. Quick Start bucket name
      can include numbers, lowercase letters, uppercase letters, and hyphens (-).
      It cannot start or end with a hyphen (-).
    Type: String
  QSS3KeyPrefix:
    AllowedPattern: ^[0-9a-zA-Z-/]*$
    ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters,
      uppercase letters, hyphens (-), and forward slash (/).
    Default: quickstart-microsoft-activedirectory/
    Description: S3 key prefix for the Quick Start assets. Quick Start key prefix
      can include numbers, lowercase letters, uppercase letters, hyphens (-), and
      forward slash (/).
    Type: String
  VPCCIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/16
    Description: CIDR Block for the VPC
    Type: String
  VPCID:
    Description: ID of the VPC (e.g., vpc-0343606e)
    Type: AWS::EC2::VPC::Id
  WS2019FULLBASE:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base'
Rules:
  SubnetsInVPC:
    Assertions:
      - Assert: !EachMemberIn
          - !ValueOfAll
            - AWS::EC2::Subnet::Id
            - VpcId
          - !RefAll 'AWS::EC2::VPC::Id'
        AssertDescription: All subnets must in the VPC
Conditions:
  GovCloudCondition: !Equals
    - !Ref 'AWS::Region'
    - us-gov-west-1
Resources:
  DHCPOptions:
    Type: AWS::EC2::DHCPOptions
    DependsOn: 
      - MicrosoftAD
      - SSMWaitCondition
    Properties:
      DomainName: !Ref 'DomainDNSName'
      DomainNameServers: !GetAtt 'MicrosoftAD.DnsIpAddresses'
      Tags:
        - Key: Domain
          Value: !Ref 'DomainDNSName'
  ADAdminSecrets:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub 'ADAdminSecret-${AWS::StackName}'
      Description: Admin User Seccrets for Manged AD Quick Start
      SecretString: !Sub '{"username":"Admin","password":"${DomainAdminPassword}"}'
  MicrosoftAD:
    Type: AWS::DirectoryService::MicrosoftAD
    Properties:
      Name: !Ref 'DomainDNSName'
      Edition: !Ref 'ADEdition'
      ShortName: !Ref 'DomainNetBIOSName'
      Password: !Ref 'DomainAdminPassword'
      VpcSettings:
        SubnetIds:
          - !Ref 'PrivateSubnet1ID'
          - !Ref 'PrivateSubnet2ID'
        VpcId: !Ref 'VPCID'
  DomainMemberSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Domain Members
      VpcId: !Ref 'VPCID'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5985
          ToPort: 5985
          CidrIp: !Ref 'PrivateSubnet1CIDR'
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: !Ref 'PrivateSubnet1CIDR'
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: !Ref 'PrivateSubnet1CIDR'
        - IpProtocol: tcp
          FromPort: 49152
          ToPort: 65535
          CidrIp: !Ref 'PrivateSubnet1CIDR'
        - IpProtocol: udp
          FromPort: 49152
          ToPort: 65535
          CidrIp: !Ref 'PrivateSubnet1CIDR'
        - IpProtocol: tcp
          FromPort: 5985
          ToPort: 5985
          CidrIp: !Ref 'PrivateSubnet2CIDR'
        - IpProtocol: tcp
          FromPort: 53
          ToPort: 53
          CidrIp: !Ref 'PrivateSubnet2CIDR'
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: !Ref 'PrivateSubnet2CIDR'
        - IpProtocol: tcp
          FromPort: 49152
          ToPort: 65535
          CidrIp: !Ref 'PrivateSubnet2CIDR'
        - IpProtocol: udp
          FromPort: 49152
          ToPort: 65535
          CidrIp: !Ref 'PrivateSubnet2CIDR'
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !Ref 'PublicSubnet1CIDR'
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !Ref 'PublicSubnet2CIDR'
        - IpProtocol: tcp
          FromPort: 636
          ToPort: 636
          CidrIp: !Ref VPCCIDR
        - IpProtocol: tcp
          FromPort: 3269
          ToPort: 3269
          CidrIp: !Ref VPCCIDR
  SSMAutomationRole:
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - s3:GetObject
                Resource: !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*'
                Effect: Allow
          PolicyName: aws-quick-start-s3-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - cloudformation:SignalResource
                Resource: !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*'
          PolicyName: aws-quick-start-cfn-signal-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - iam:CreateRole
                  - iam:PutRolePolicy
                  - iam:getRolePolicy
                  - iam:DetachRolePolicy
                  - iam:AttachRolePolicy
                  - iam:DeleteRolePolicy
                  - iam:CreateInstanceProfile
                  - iam:DeleteRole
                  - iam:RemoveRoleFromInstanceProfile
                  - iam:AddRoleToInstanceProfile
                  - iam:DeleteInstanceProfile
                  - iam:PassRole
                Resource: '*'
          PolicyName: aws-quick-start-create-role-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                  - secretsmanager:DescribeSecret
                Resource: 
                  - !Ref 'ADAdminSecrets'
              - Effect: Allow
                Action:
                  - ssm:StartAutomationExecution
                Resource: '*'
          PolicyName: AD-SSM-Secrets
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: 
                - ssm.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMFullAccess'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AWSCloudFormationFullAccess'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonEC2FullAccess'
  LambdaSSMRole:
    DependsOn: MicrosoftAD
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyDocument:
           Version: '2012-10-17'
           Statement:
             - Effect: Allow
               Action:
                 - iam:PassRole
               Resource: !GetAtt SSMAutomationRole.Arn
          PolicyName: QS-SSM-PassRole
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: 
                - lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole'
  DNSForwarderSetup:
    Type: AWS::SSM::Document
    Properties:
      DocumentType: Automation
      Content:
        schemaVersion: "0.3"
        description: Setup DNS Forwarder for AWS Managed AD to VPC DNS
        assumeRole: "{{AutomationAssumeRole}}"
        parameters:
          StackName:
            description: "Stack Name Input for cfn resource signal"
            type: "String"
          DomainMemberSG:
            description: Security group ID that can communicate with domain controllers
            type: "String"
          VPCCIDR:
            description: VPC CIDR
            type: "String"
          QSS3BucketName:
            description: "S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)."
            type: "String"
          QSS3KeyPrefix:
            description: "S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)."
            type: "String"
          PrivateSubnet1ID:
            description: "Private Subnet 1 ID"
            type: "String"
          AutomationAssumeRole:
            description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
            type: "String"
          DirectoryID:
            description: "Alias of the directory ID"
            type: "String"
          AWSRegion:
            description: "Region"
            type: "String"
          URLSuffix:
            default: "amazonaws.com"
            description: "AWS URL suffix"
            type: "String"
        mainSteps:
        - name:  CreateStack
          action: aws:createStack
          onFailure: "step:CFNSignalEnd"
          inputs:
            StackName: "SetDNSForwarder"
            Capabilities: [ "CAPABILITY_IAM" ]
            TemplateBody: |
              Description: "Deploy Instance to create DNS forwarder on AWS Managed AD"
              Parameters:
                WINFULLBASE:
                  Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
                  Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base'
                QSS3BucketName:
                  Type: "String"
                  Default: "{{QSS3BucketName}}"
                  Description: "Name of Target S3 Bucket"
                QSS3KeyPrefix:
                  Type: "String"
                  Default: "{{QSS3KeyPrefix}}"
                  Description: "Name of Target S3 Prefix"
                SecurityGroup:
                  Description: Security Group to be able to talk Domain Controllers
                  Default: "{{DomainMemberSG}}"
                  Type: "String"
                Subnet:
                  Description: "Subnet to deploy the EC2 instnace"
                  Default: "{{PrivateSubnet1ID}}"
                  Type: "String"
              Resources:
                ForwarderRole: 
                  Type : AWS::IAM::Role
                  Properties:
                    Policies:
                      - PolicyDocument:
                          Version: '2012-10-17'
                          Statement:
                            - Action:
                                - s3:GetObject
                                - s3:ListBucket
                              Resource:
                                - !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*'
                                - !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}'
                              Effect: Allow
                        PolicyName: s3-instance-bucket-policy
                    Path: /
                    ManagedPolicyArns:
                    - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'
                    - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy'
                    - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AWSDirectoryServiceFullAccess'
                    AssumeRolePolicyDocument:
                      Version: "2012-10-17"
                      Statement:
                      - Effect: "Allow"
                        Principal:
                          Service:
                          - "ec2.amazonaws.com"
                          - "ssm.amazonaws.com"
                        Action: "sts:AssumeRole"
                IamInstanceProfile:
                  Type: "AWS::IAM::InstanceProfile"
                  Properties:
                    Roles:
                    - !Ref ForwarderRole
                EC2Instance:
                  Type: "AWS::EC2::Instance"
                  Properties:
                    ImageId: !Ref WINFULLBASE
                    InstanceType: "t3.medium"
                    IamInstanceProfile: !Ref IamInstanceProfile
                    SecurityGroupIds:
                      - !Ref 'SecurityGroup'
                    SubnetId: !Ref Subnet
                    Tags:
                    - Key: "Name"
                      Value: "TempDNSForwarder"
        - name: "getInstanceId"
          action: aws:executeAwsApi
          onFailure: "step:CFNSignalEnd"
          inputs:
            Service: ec2
            Api: DescribeInstances
            Filters:  
            - Name: "tag:Name"
              Values: [ "TempDNSForwarder" ]
            - Name: "instance-state-name"
              Values: [ "running" ]
          outputs:
          - Name: InstanceId
            Selector: "$.Reservations[0].Instances[0].InstanceId"
            Type: String
        - name: "CreateDNSForward"
          action: "aws:runCommand"
          onFailure: "step:signalfailure"
          inputs:
            DocumentName: "AWS-RunRemoteScript"
            InstanceIds:
              - "{{getInstanceId.InstanceId}}"
            CloudWatchOutputConfig:
              CloudWatchOutputEnabled: "true"
              CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}'
            Parameters:
              sourceType: "S3"
              sourceInfo: 
                !Sub '{"path": "https://${QSS3BucketName}.s3.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/AddDNSForward.ps1"}'
              commandLine: "./AddDNSForward.ps1 -DirectoryID {{DirectoryID}} -VPCCIDR {{VPCCIDR}} -AWSRegion {{AWSRegion}}"
        # Determines if CFN Needs to be Signaled or if Work flow should just end
        - name: CFNSignalEnd
          action: aws:branch
          inputs:
            Choices:
            - NextStep: signalsuccess
              Not: 
                Variable: "{{StackName}}"
                StringEquals: ""
            - NextStep: sleepend
              Variable: "{{StackName}}"
              StringEquals: ""
        # If all steps complete successfully signals CFN of Success
        - name: "signalsuccess"
          action: "aws:executeAwsApi"
          nextStep: "deleteStack"
          inputs:
            Service: cloudformation
            Api: SignalResource
            LogicalResourceId: "SSMWaitCondition"
            StackName: "{{StackName}}"
            Status: SUCCESS
            UniqueId: "{{getInstanceId.InstanceId}}"
        # If CFN Signl Not Needed this sleep ends work flow
        - name: "sleepend"
          action: "aws:sleep"
          nextStep: "deleteStack"
          inputs:
            Duration: PT1S
        # If any steps fails signals CFN of Failure
        - name: "signalfailure"
          action: "aws:executeAwsApi"
          nextStep: "deleteStack"
          inputs:
            Service: cloudformation
            Api: SignalResource
            LogicalResourceId: "SSMWaitCondition"
            StackName: "{{StackName}}"
            Status: FAILURE
            UniqueId: "{{getInstanceId.InstanceId}}"
        - name: deleteStack
          action: aws:deleteStack
          isEnd: true
#          onFailure: Continue
          inputs:
            StackName: "SetDNSForwarder"
  LambdaSSMExecute:
    DependsOn: LambdaSSMRole
    Type: AWS::Lambda::Function
    Properties:
      Description: Executes SSM Automation Documents
      Handler: index.handler
      Runtime: python3.7
      Role: !GetAtt LambdaSSMRole.Arn
      Timeout: 900
      Code:
        ZipFile: |
          def handler(event, context):
              import cfnresponse
              import boto3, os, json
              from botocore.vendored import requests

              ssm_cl = boto3.client('ssm')
              ecr_cl = boto3.client('ecr')
              req_type = event['RequestType']
              print(event)

              SUCCESS = "SUCCESS"
              FAILED = "FAILED"

              def start_ssmautomation(event):
                  doc_name = event['ResourceProperties']['DocumentName']
                  stack_name = event['ResourceProperties']['StackName']
                  ssm_role = event['ResourceProperties']['AutomationAssumeRole']
                  qs_bucket = event['ResourceProperties']['QSS3BucketName']
                  qs_bucket_prefix = event['ResourceProperties']['QSS3KeyPrefix']
                  security_group = event['ResourceProperties']['DomainMemberSG']
                  subnet_id = event['ResourceProperties']['PrivateSubnet1ID']
                  vpc_cidr = event['ResourceProperties']['VPCCIDR']
                  directory_id = event['ResourceProperties']['DirectoryID']
                  aws_region = event['ResourceProperties']['AWSRegion']
                  url_suffix = event['ResourceProperties']['URLSuffix']

                  start_automation = ssm_cl.start_automation_execution(
                      DocumentName= doc_name,
                      Parameters={
                          'StackName': [
                              stack_name
                          ],
                          'AutomationAssumeRole': [
                              ssm_role
                          ],
                          'QSS3BucketName': [
                              qs_bucket
                          ],
                          'QSS3KeyPrefix': [
                              qs_bucket_prefix
                          ],
                          'DomainMemberSG': [
                              security_group
                          ],
                          'PrivateSubnet1ID': [
                              subnet_id
                          ],
                          'VPCCIDR': [
                              vpc_cidr
                          ],
                          'DirectoryID': [
                              directory_id
                          ],
                          'AWSRegion': [
                              aws_region
                          ],
                          'URLSuffix': [
                              url_suffix
                          ]
                      },
                  )

                  cfnresponse.send(event, context, SUCCESS, start_automation, start_automation['AutomationExecutionId'])

              def delete_image(event):
                  cfnresponse.send(event, context, SUCCESS, event, "Deleted")

              actions = {
                  'Create': start_ssmautomation,
                  'Delete': delete_image,
                  'Update': start_ssmautomation
              }

              try:
                  actions.get(req_type)(event)    
              except Exception as exc:
                  error_msg = {'Error': '{}'.format(exc)}
                  print(error_msg)
                  cfnresponse.send(event, context, FAILED, error_msg)
  ExecuteSSMAutomation:
    DependsOn: LambdaSSMExecute
    Type: Custom::ExecuteSSMAutomation
    Properties: 
      ServiceToken: !GetAtt LambdaSSMExecute.Arn
      DocumentName: !Ref DNSForwarderSetup
      DomainMemberSG: !Ref DomainMemberSG
      StackName: !Ref AWS::StackName
      QSS3BucketName: !Ref QSS3BucketName
      QSS3KeyPrefix: !Ref QSS3KeyPrefix
      AutomationAssumeRole: !GetAtt SSMAutomationRole.Arn
      PrivateSubnet1ID: !Ref PrivateSubnet1ID
      VPCCIDR: !Ref VPCCIDR
      DirectoryID: !GetAtt 'MicrosoftAD.Alias'
      AWSRegion: !Ref 'AWS::Region'
      URLSuffix: !Ref 'AWS::URLSuffix'
  SSMWaitHandle: 
    Type: AWS::CloudFormation::WaitConditionHandle
  SSMWaitCondition: 
    Type: AWS::CloudFormation::WaitCondition
    CreationPolicy:
      ResourceSignal:
        Timeout: PT60M
        Count: 1
    DependsOn: 
      - ExecuteSSMAutomation
      - SSMWaitHandle
    Properties: 
      Handle: 
        Ref: "SSMWaitHandle"
      Timeout: "3600"
      Count: 1
  AWSQuickstartADRole:
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - s3:GetObject
                  - s3:ListBucket
                Resource: 
                  - !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*'
                  - !Sub 'arn:${AWS::Partition}:s3:::${QSS3BucketName}'
                Effect: Allow
          PolicyName: aws-quick-start-s3-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - cloudformation:SignalResource
                Resource: !Sub 'arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*'
              - Effect: Allow
                Action:
                  - ec2:DescribeInstances
                  - ec2:DescribeInstanceStatus
                  - ssm:*
                Resource: '*'
          PolicyName: AD-SSM-AutomationExecution
      Path: /
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
                - ssm.amazonaws.com
            Effect: Allow
        Version: '2012-10-17'

Outputs:
  ADServer1PrivateIP:
    Value: !Select
      - '0'
      - !GetAtt 'MicrosoftAD.DnsIpAddresses'
    Description: AD Server 1 Private IP Address (this may vary based on Directory
      Service order of IP addresses)
  ADServer2PrivateIP:
    Value: !Select
      - '1'
      - !GetAtt 'MicrosoftAD.DnsIpAddresses'
    Description: AD Server 2 Private IP Address (this may vary based on Directory
      Service order of IP addresses)
  DirectoryID:
    Value: !Ref 'MicrosoftAD'
    Description: Directory Services ID
  DomainAdmin:
    Value: !Join
      - ''
      - - !Ref 'DomainNetBIOSName'
        - \admin
    Description: Domain administrator account
  DomainMemberSGID:
    Value: !Ref 'DomainMemberSG'
    Description: Domain Member Security Group ID
  ADSecretsArn:
    Value: !Ref 'ADAdminSecrets'
    Description: Managed AD Admin Secrets

ADStack

AWSTemplateFormatVersion: '2010-09-09'
Description: This template creates 1 Windows 2019 Active Directory Domain Controllers
  into private subnets in separate Availability Zones inside a VPC. The default Domain
  Administrator password will be the one retrieved from the instance.  For adding
  members to the domain, ensure that they are launched into the domain member security
  group created by this template and then configure them to use the AD instances fixed
  private IP addresses as the DNS server. **WARNING** This template creates Amazon
  EC2 Windows instance and related resources. You will be billed for the AWS resources
  used if you create a stack from this template. QS(0001)
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Network Configuration
        Parameters:
          - VPCCIDR
          - VPCID
          - PrivateSubnet1ID
      - Label:
          default: Amazon EC2 Configuration
        Parameters:
          - ADServer1InstanceType
          - ADServer1NetBIOSName
          - ADServer1PrivateIP
          - ADServer2InstanceType
          - ADServer2NetBIOSName
          - ADServer2PrivateIP
          - WS2016FULLBASE
      - Label:
          default: Microsoft Active Directory Configuration
        Parameters:
          - DomainAdminPassword
          - DomainAdminUser
          - DomainDNSName
          - DomainNetBIOSName
      - Label:
          default: AWS Quick Start Configuration
        Parameters:
          - QSS3BucketName
          - QSS3KeyPrefix
    ParameterLabels:
      ADServer1InstanceType:
        default: Domain Controller 1 Instance Type
      ADServer1NetBIOSName:
        default: Domain Controller 1 NetBIOS Name
      ADServer1PrivateIP:
        default: Domain Controller 1 Private IP Address
      DomainAdminPassword:
        default: Domain Admin Password
      DomainAdminUser:
        default: Domain Admin User Name
      DomainDNSName:
        default: Domain DNS Name
      DomainNetBIOSName:
        default: Domain NetBIOS Name
      WS2016FULLBASE:
        default: SSM Parameter Value to grab the lastest AMI ID
      PrivateSubnet1ID:
        default: Private Subnet 1 ID
      QSS3BucketName:
        default: Quick Start S3 Bucket Name
      QSS3KeyPrefix:
        default: Quick Start S3 Key Prefix
      VPCCIDR:
        default: VPC CIDR
      VPCID:
        default: VPC ID
Parameters:
  ADServer1InstanceType:
    AllowedValues:
      - t2.large
      - m4.large
      - m4.xlarge
      - m4.2xlarge
      - m4.4xlarge
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
    Default: m5.xlarge
    Description: Amazon EC2 instance type for the first Active Directory instance
    Type: String
  ADServer1NetBIOSName:
    AllowedPattern: '[a-zA-Z0-9\-]+'
    Default: DC1
    Description: NetBIOS name of the first Active Directory server (up to 15 characters)
    MaxLength: '15'
    MinLength: '1'
    Type: String
  ADServer1PrivateIP:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
    Default: 10.0.0.10
    Description: Fixed private IP for the first Active Directory server located in
      Availability Zone 1
    Type: String
  DomainAdminPassword:
    AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*
    Description: Password for the domain admin user. Must be at least 8 characters
      containing letters, numbers and symbols
    MaxLength: '32'
    MinLength: '8'
    NoEcho: 'true'
    Type: String
  DomainAdminUser:
    AllowedPattern: '[a-zA-Z0-9]*'
    Default: Admin
    Description: User name for the account that will be added as Domain Administrator.
      This is separate from the default "Administrator" account
    MaxLength: '25'
    MinLength: '5'
    Type: String
  DomainDNSName:
    AllowedPattern: '[a-zA-Z0-9\-]+\..+'
    Default: example.com
    Description: Fully qualified domain name (FQDN) of the forest root domain e.g.
      example.com
    MaxLength: '255'
    MinLength: '2'
    Type: String
  DomainNetBIOSName:
    AllowedPattern: '[a-zA-Z0-9\-]+'
    Default: example
    Description: NetBIOS name of the domain (up to 15 characters) for users of earlier
      versions of Windows e.g. EXAMPLE
    MaxLength: '15'
    MinLength: '1'
    Type: String
  PrivateSubnet1ID:
    Description: ID of the private subnet 1 in Availability Zone 1 (e.g., subnet-a0246dcd)
    Type: AWS::EC2::Subnet::Id
  QSS3BucketName:
    AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$
    ConstraintDescription: Quick Start bucket name can include numbers, lowercase
      letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen
      (-).
    Default: alpublic
    Description: S3 bucket name for the Quick Start assets. Quick Start bucket name
      can include numbers, lowercase letters, uppercase letters, and hyphens (-).
      It cannot start or end with a hyphen (-).
    Type: String
  QSS3KeyPrefix:
    AllowedPattern: ^[0-9a-zA-Z-/]*$
    ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters,
      uppercase letters, hyphens (-), and forward slash (/).
    Default: quickstart-microsoft-activedirectory/
    Description: S3 key prefix for the Quick Start assets. Quick Start key prefix
      can include numbers, lowercase letters, uppercase letters, hyphens (-), and
      forward slash (/).
    Type: String
  VPCCIDR:
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    Default: 10.0.0.0/16
    Description: CIDR Block for the VPC
    Type: String
  VPCID:
    Description: ID of the VPC (e.g., vpc-0343606e)
    Type: AWS::EC2::VPC::Id
  WS2016FULLBASE:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: '/aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base'
Rules:
  SubnetsInVPC:
    Assertions:
      - Assert: !EachMemberIn
          - !ValueOfAll
            - AWS::EC2::Subnet::Id
            - VpcId
          - !RefAll 'AWS::EC2::VPC::Id'
        AssertDescription: All subnets must in the VPC
Conditions:
  GovCloudCondition: !Equals
    - !Ref 'AWS::Region'
    - us-gov-west-1
Resources:
  DSCBucket:
    Type: AWS::S3::Bucket
    Properties:
      LifecycleConfiguration:
        Rules:
          - Id: DeleteAfter30Days
            ExpirationInDays: 30
            Status: Enabled
            Prefix: 'logs/'
  DHCPOptions:
    Type: AWS::EC2::DHCPOptions
    DependsOn:
      - DomainController1
    Properties:
      DomainName: !Ref 'DomainDNSName'
      DomainNameServers:
        - !Ref 'ADServer1PrivateIP'
      Tags:
        - Key: Domain
          Value: !Ref 'DomainDNSName'
  VPCDHCPOptionsAssociation:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    Properties:
      VpcId: !Ref 'VPCID'
      DhcpOptionsId: !Ref 'DHCPOptions'
  AWSQuickstartActiveDirectoryDS:
    Type: AWS::SSM::Document
    Properties:
      DocumentType: Automation
      Content:
        schemaVersion: "0.3"
        description: "Deploy AD with SSM Automation"
        # Role that is utilized to perform the steps within the Automation Document. In this case to be able to Signal CFN and Describe Instances. 
        assumeRole: "{{AutomationAssumeRole}}"
        # Gathering parameters needed to configure DCs in the Quick Start
        parameters: 
          ADServer1NetBIOSName:
            default: "DC1"
            description: "NetBIOS name of the first Active Directory server (up to 15 characters)"
            type: "String"
          ADServer1PrivateIP:
            default: "10.0.0.10"
            description: "Fixed private IP for the first Active Directory server located in Availability Zone 1"
            type: "String"
          VPCCIDR:
            default: '10.0.0.0/16'
            description: "CIDR block for private subnet 1 located in Availability Zone 1."
            type: "String"
          ADAdminSecParamName:
            description: "AWS Secrets Parameter Name that has Password and User namer for the domain administrator."
            type: "String"
          ADAltUserSecParamName:
            description: "AWS Secrets Parameter Name for the account that will be added as Domain Administrator. This is separate from the default Administrator account"
            type: "String"
          RestoreModeSecParamName:
            description: "AWS Secrets Parameter Name for the Restore Mode Password"
            type: "String"
          DomainDNSName: 
            default: "example.com"
            description: "Fully qualified domain name (FQDN) of the forest root domain e.g. example.com"
            type: "String"
          DomainNetBIOSName: 
            default: "example"
            description: "NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows e.g. EXAMPLE"
            type: "String"
          QSS3BucketName:
            default: "aws-quickstart"
            description: "S3 bucket name for the Quick Start assets. Quick Start bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-)."
            type: "String"
          QSS3KeyPrefix:
            default: "quickstart-microsoft-activedirectory/"
            description: "S3 key prefix for the Quick Start assets. Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/)."
            type: "String"
          URLSuffix:
            default: "amazonaws.com"
            description: "AWS URL suffix"
            type: "String"
          StackName:
            default: ""
            description: "Stack Name Input for cfn resource signal"
            type: "String"
          AutomationAssumeRole:
            default: ""
            description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
            type: "String"
        mainSteps:
        # This step grabs the Instance IDs for both nodes that will be configured as DCs in the Quick Start and Instance IDs for the for next steps.
        - name: "dc1InstanceId"
          action: aws:executeAwsApi
          onFailure: "step:signalfailure"
          inputs:
            Service: ec2
            Api: DescribeInstances
            Filters:  
            - Name: "tag:Name"
              Values: [ "{{ADServer1NetBIOSName}}" ]
            - Name: "tag:aws:cloudformation:stack-name"
              Values: ["{{StackName}}"]
            - Name: "instance-state-name"
              Values: [ "running" ]
          outputs:
          - Name: InstanceId
            Selector: "$.Reservations[0].Instances[0].InstanceId"
            Type: "String"
        # Installs needed Powershell DSC Modules and components on both nodes.
        - name: "dcInstallDscModules"
          action: "aws:runCommand"
          onFailure: "step:signalfailure"
          inputs:
            DocumentName: "AWS-RunRemoteScript"
            InstanceIds:
            - "{{dc1InstanceId.InstanceId}}"
            CloudWatchOutputConfig:
              CloudWatchOutputEnabled: "true"
              CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}'
            Parameters:
              sourceType: "S3"
              sourceInfo: !Sub '{"path": "https://${QSS3BucketName}.s3.amazonaws.com/{{QSS3KeyPrefix}}scripts/install-ad-modules.ps1"}'
              commandLine: "./install-ad-modules.ps1"
        # Configures Local Configuration Manager on each of the nodes.
        - name: "dcsLCMConfig"
          action: "aws:runCommand"
          onFailure: "step:signalfailure"
          inputs:
            DocumentName: "AWS-RunRemoteScript"
            InstanceIds:
            - "{{dc1InstanceId.InstanceId}}"
            CloudWatchOutputConfig:
              CloudWatchOutputEnabled: "true"
              CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}'
            Parameters:
              sourceType: "S3"
              sourceInfo: !Sub '{"path": "https://${QSS3BucketName}.s3.amazonaws.com/{{QSS3KeyPrefix}}scripts/LCM-Config.ps1"}'
              commandLine: "./LCM-Config.ps1"
        # Generates MOF file on first DC Node to be processed by LCM.
        - name: "createDC1Mof"
          action: "aws:runCommand"
          onFailure: "step:signalfailure"
          nextStep: "configDC1"
          inputs:
            DocumentName: "AWS-RunRemoteScript"
            InstanceIds:
            - "{{dc1InstanceId.InstanceId}}"
            CloudWatchOutputConfig:
              CloudWatchOutputEnabled: "true"
              CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}'
            Parameters:
              sourceType: "S3"
              sourceInfo: !Sub '{"path": "https://${QSS3BucketName}.s3.amazonaws.com/{{QSS3KeyPrefix}}scripts/ConfigDC1.ps1"}'
              commandLine: "./ConfigDC1.ps1 -ADServer1NetBIOSName {{ADServer1NetBIOSName}} -DomainNetBIOSName {{DomainNetBIOSName}} -DomainDNSName {{DomainDNSName}} -ADAdminSecParam {{ADAdminSecParamName}} -ADAltUserSecParam {{ADAltUserSecParamName}} -RestoreModeSecParam {{RestoreModeSecParamName}} -SiteName {{global:REGION}} -VPCCIDR {{VPCCIDR}}"
        # Kicks off DSC Configuration and loops\reboots until Node matches Configuration defined in MOF file.
        - name: "configDC1"
          action: aws:runCommand
          onFailure: "step:signalfailure"
          inputs:
            DocumentName: AWS-RunPowerShellScript
            InstanceIds: 
              - "{{dc1InstanceId.InstanceId}}"
            CloudWatchOutputConfig:
              CloudWatchOutputEnabled: "true"
              CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}'
            Parameters:
              commands: 
                - |     
                   function DscStatusCheck () {
                       $LCMState = (Get-DscLocalConfigurationManager).LCMState
                       if ($LCMState -eq 'PendingConfiguration' -Or $LCMState -eq 'PendingReboot') {
                           'returning 3010, should continue after reboot'
                           exit 3010
                       } else {
                           'Completed'
                       }
                   }

                   Start-DscConfiguration 'C:\AWSQuickstart\ConfigDC1' -Wait -Verbose -Force

                   DscStatusCheck
        # Ensure that AD servers point to themselves for DNS
        - name: "DnsConfig"
          action: "aws:runCommand"
          onFailure: "step:signalfailure"
          inputs:
            DocumentName: "AWS-RunRemoteScript"
            InstanceIds:
              - "{{dc1InstanceId.InstanceId}}"
            CloudWatchOutputConfig:
              CloudWatchOutputEnabled: "true"
              CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}'
            Parameters:
              sourceType: S3
              sourceInfo: !Sub '{"path": "https://${QSS3BucketName}.s3.amazonaws.com/{{QSS3KeyPrefix}}scripts/Dns-Config.ps1"}'
              commandLine: "./Dns-Config.ps1 -ADServer1NetBIOSName {{ADServer1NetBIOSName}} -ADServer1PrivateIP {{ADServer1PrivateIP}} -DomainDNSName {{DomainDNSName}} -ADAdminSecParam {{ADAdminSecParamName}}"
        # Determines if CFN Needs to be Signaled or if Work flow should just end
        - name: CFNSignalEnd
          action: aws:branch
          inputs:
            Choices:
            - NextStep: signalsuccess
              Not: 
                Variable: "{{StackName}}"
                StringEquals: ""
            - NextStep: sleepend
              Variable: "{{StackName}}"
              StringEquals: ""
        # If all steps complete successfully signals CFN of Success
        - name: "signalsuccess"
          action: "aws:executeAwsApi"
          isEnd: True
          inputs:
            Service: cloudformation
            Api: SignalResource
            LogicalResourceId: "DomainController1"
            StackName: "{{StackName}}"
            Status: SUCCESS
            UniqueId: "{{dc1InstanceId.InstanceId}}"
        # If CFN Signl Not Needed this sleep ends work flow
        - name: "sleepend"
          action: "aws:sleep"
          isEnd: True
          inputs:
            Duration: PT1S
        # If any steps fails signals CFN of Failure
        - name: "signalfailure"
          action: "aws:executeAwsApi"
          inputs:
            Service: cloudformation
            Api: SignalResource
            LogicalResourceId: "DomainController2"
            StackName: "{{StackName}}"
            Status: FAILURE
            UniqueId: "{{dc1InstanceId.InstanceId}}"
  AWSQuickstartADDSRole:
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - s3:GetObject
                  - s3:ListBucket
                Resource:
                  - !Sub arn:${AWS::Partition}:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*
                  - !Sub arn:${AWS::Partition}:s3:::${QSS3BucketName}
                Effect: Allow
          PolicyName: aws-quick-start-s3-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - cloudformation:SignalResource
                Resource: !Sub 'arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*'
              - Effect: Allow
                Action:
                  - ec2:DescribeInstances
                  - ec2:DescribeInstanceStatus
                  - ssm:*
                Resource: '*'
          PolicyName: AD-SSM-Automation
      Path: /
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
                - ssm.amazonaws.com
            Effect: Allow
        Version: '2012-10-17'
  ADSsmPassRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: AD-SSM-PassRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - iam:PassRole
            Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/${AWSQuickstartADDSRole}'
      Roles:
        - !Ref 'AWSQuickstartADDSRole'
  ADServerRole:
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - s3:GetObject
                  - s3:ListBucket
                Resource: 
                  - !Sub arn:${AWS::Partition}:s3:::${QSS3BucketName}/${QSS3KeyPrefix}*
                  - !Sub arn:${AWS::Partition}:s3:::${QSS3BucketName}
                Effect: Allow
          PolicyName: aws-quick-start-s3-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action:
                  - s3:GetObject
                Resource: 
                  - !Sub 'arn:aws:s3:::aws-ssm-${AWS::Region}/*'
                  - !Sub 'arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*'
                  - !Sub 'arn:aws:s3:::amazon-ssm-${AWS::Region}/*'
                  - !Sub 'arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*'
                  - !Sub 'arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*'
                  - !Sub 'arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*'
                Effect: Allow
          PolicyName: ssm-custom-s3-policy
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:GetSecretValue
                  - secretsmanager:DescribeSecret
                Resource: 
                  - !Ref 'ADAdminSecrets'
                  - !Ref 'RestoreModeSecrets'
                  - !Ref 'ADAltUserSecrets'
              - Effect: Allow
                Action:
                  - ssm:StartAutomationExecution
                Resource: '*'
          PolicyName: AD-SSM-Secrets
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/${AWSQuickstartADDSRole}'
          PolicyName: AD-SSM-PassRole
      Path: /
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy'
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - sts:AssumeRole
            Principal:
              Service:
                - ec2.amazonaws.com
            Effect: Allow
        Version: '2012-10-17'
  ADServerProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref 'ADServerRole'
      Path: /
  ADAdminSecrets:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub 'ADAdministratorSecret-${AWS::StackName}'
      Description: Administrator Password for AD Quick Start
      GenerateSecretString:
        SecretStringTemplate: '{"username": "Administrator"}'
        GenerateStringKey: "password"
        PasswordLength: 30
        ExcludeCharacters: '"@/\'
  RestoreModeSecrets:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub 'RestoreModeSecrets-${AWS::StackName}'
      Description: Restore Mode Password for AD Quick Start
      GenerateSecretString:
        SecretStringTemplate: '{"username": "Administrator"}'
        GenerateStringKey: "password"
        PasswordLength: 30
        ExcludeCharacters: '"@/\'
  ADAltUserSecrets:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub 'ADAltUserSecrets-${AWS::StackName}'
      Description: Alternate AD Admin User from AD Quick Start
      SecretString: !Sub '{"username":"${DomainAdminUser}","password":"${DomainAdminPassword}"}'
  DomainJoinSecrets:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Sub 'DomainJoinSecrets-${AWS::StackName}'
      Description: Alternate AD Admin User from AD Quick Start
      SecretString: !Sub '{"username":"${DomainNetBIOSName}\\${DomainAdminUser}","password":"${DomainAdminPassword}"}'
  DomainNameParam:
    Type: "AWS::SSM::Parameter"
    Properties:
      Name: "/demo/AD/DomainName"
      Type: "String"
      Value: !Ref 'DomainDNSName'
      Description: "SSM Parameter for Domain Name."
  DomainController1:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Timeout: PT60M
        Count: 1
    Properties:
      ImageId: !Ref 'WS2016FULLBASE'
      IamInstanceProfile: !Ref 'ADServerProfile'
      InstanceType: !Ref 'ADServer1InstanceType'
      SubnetId: !Ref 'PrivateSubnet1ID'
      Tags:
        - Key: Name
          Value: !Ref 'ADServer1NetBIOSName'
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeSize: '100'
            VolumeType: gp2
      SecurityGroupIds:
        - !Ref 'DomainControllersSG'
      PrivateIpAddress: !Ref 'ADServer1PrivateIP'
      UserData: !Base64
        Fn::Join:
          - ''
          - - "<powershell>\n"
            - 'Start-SSMAutomationExecution -DocumentName '
            - !Sub '"${AWSQuickstartActiveDirectoryDS}"'
            - ' -Parameter @{'
            - '"ADServer1NetBIOSName"='
            - !Sub '"${ADServer1NetBIOSName}"'
            - ';"ADServer1PrivateIP"='
            - !Sub '"${ADServer1PrivateIP}"'
            - ';"DomainDNSName"='
            - !Sub '"${DomainDNSName}"'
            - ';"DomainNetBIOSName"='
            - !Sub '"${DomainNetBIOSName}"'
            - ';"VPCCIDR"='
            - !Sub '"${VPCCIDR}"'
            - ';"QSS3BucketName"='
            - !Sub '"${QSS3BucketName}"'
            - ';"QSS3KeyPrefix"='
            - !Sub '"${QSS3KeyPrefix}"'
            - ';"ADAdminSecParamName"='
            - !Sub '"${ADAdminSecrets}"'
            - ';"ADAltUserSecParamName"='
            - !Sub '"${ADAltUserSecrets}"'
            - ';"RestoreModeSecParamName"='
            - !Sub '"${RestoreModeSecrets}"'
            - ';"StackName"='
            - !Sub '"${AWS::StackName}"'
            - ';"URLSuffix"='
            - !Sub '"${AWS::URLSuffix}"'
            - ';"AutomationAssumeRole"='
            - !Sub '"arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${AWSQuickstartADDSRole}"'
            - '}'
            - "\n"
            - "</powershell>\n"
  DomainControllersSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Domain Controllers Security Group
      VpcId:
        Ref: VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5985
          ToPort: 5985
          CidrIp: !Ref VPCCIDR
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref VPCCIDR
        - IpProtocol: tcp
          FromPort: 53 
          ToPort: 53
          CidrIp: !Ref VPCCIDR
        - IpProtocol: udp
          FromPort: 53 
          ToPort: 53
          CidrIp: !Ref VPCCIDR
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !Ref VPCCIDR
        - IpProtocol: udp
          FromPort: 123
          ToPort: 123
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 135
          ToPort: 135
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 9389
          ToPort: 9389
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 138
          ToPort: 138
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 445
          ToPort: 445
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 445
          ToPort: 445
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 464
          ToPort: 464
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 464
          ToPort: 464
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 49152
          ToPort: 65535
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 49152
          ToPort: 65535
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 389
          ToPort: 389
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 389
          ToPort: 389
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 636
          ToPort: 636
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 3268
          ToPort: 3268
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 3269
          ToPort: 3269
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 9389
          ToPort: 9389
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 88
          ToPort: 88
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 88
          ToPort: 88
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 5355
          ToPort: 5355
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: udp
          FromPort: 137
          ToPort: 137
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: tcp
          FromPort: 139 
          ToPort: 139
          SourceSecurityGroupId: !Ref DomainMembersSG 
        - IpProtocol: tcp
          FromPort: 5722
          ToPort: 5722
          SourceSecurityGroupId: !Ref DomainMembersSG
        - IpProtocol: icmp
          FromPort: -1 
          ToPort: -1 
          SourceSecurityGroupId: !Ref DomainMembersSG
  DomainMembersSG: 
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: Domain Members
      VpcId: !Ref VPCID
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5985
          ToPort: 5985
          CidrIp: !Ref VPCCIDR
        - IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !Ref VPCCIDR
  DCSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      Description: Security Group Rule between Domain Controllers 
      GroupId: !Ref DomainControllersSG
      IpProtocol: -1 
      FromPort: -1 
      ToPort: -1 
      SourceSecurityGroupId: !Ref DomainControllersSG
  LambdaSSMRole:
    Type: AWS::IAM::Role
    Properties:
      Policies:
        - PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Effect: Allow
              Action:
              - s3:PutObject
              Resource:
                - !Sub "${DSCBucket.Arn}"
                - !Sub "${DSCBucket.Arn}/*"
          PolicyName: write-mof-s3
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: 
                - lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
  WriteMOFFunction:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        ZipFile: >
          var AWS = require('aws-sdk'), s3 = new AWS.S3();
          const response = require("cfn-response");
          exports.handler = async (event, context) => {
            console.log(JSON.stringify(event));
            if (event.RequestType === 'Delete') {
                await postResponse(event, context, response.SUCCESS, {})
                return;
            }
            function postResponse(event, context, status, data){
                return new Promise((resolve, reject) => {
                    setTimeout(() => response.send(event, context, status, data), 5000)
                });
            }
            await s3.putObject({
              Body: event.ResourceProperties.Body,
              Bucket: event.ResourceProperties.Bucket,
              Key: event.ResourceProperties.Key
            }).promise();
            await postResponse(event, context, response.SUCCESS, {});
          };
      Handler: index.handler
      Role: !GetAtt LambdaSSMRole.Arn
      Runtime: nodejs10.x
      Timeout: 10
  WriteDomainJoinMOF:
    Type: Custom::WriteMOFFile
    Properties:
      ServiceToken: !GetAtt WriteMOFFunction.Arn
      Bucket: !Ref DSCBucket
      Key: !Sub "DomainJoin-${AWS::StackName}.mof"
      Body: !Sub |
        /*
        @TargetNode='localhost'
        */
        instance of MSFT_Credential as $MSFT_Credential1ref
        {
        Password = "managementgovernancesample";
         UserName = "${DomainJoinSecrets}";

        };
        instance of DSC_Computer as $DSC_Computer1ref
        {
        ResourceID = "[Computer]JoinDomain";
         Credential = $MSFT_Credential1ref;
         DomainName = "{tag:DomainToJoin}";
         Name = "{tag:Name}";
         ModuleName = "ComputerManagementDsc";
         ModuleVersion = "8.0.0";
         ConfigurationName = "DomainJoin";
        };
        instance of OMI_ConfigurationDocument
                            {
         Version="2.0.0";
                                MinimumCompatibleVersion = "1.0.0";
                                CompatibleVersionAdditionalProperties= {"Omi_BaseResource:ConfigurationName"};
                                Name="DomainJoin";
                            };      
  DomainJoinAssociation:
    Type: AWS::SSM::Association
    Properties:
      # We are using the AWS-ApplyDSCMofs Document
      Name: AWS-ApplyDSCMofs
      Targets:
        - Key: "tag:DomainToJoin"
          Values:
           - !Ref "DomainDNSName"
      OutputLocation:
        S3Location: 
          OutputS3BucketName: !Ref DSCBucket
          OutputS3KeyPrefix: 'logs/'
      # Here we have a Cron Expression that will run this everyday at 23:15 every day
      ScheduleExpression: "cron(15 23 * * ? *)"
      Parameters:
        # Here the Mof file resides in a S3 Bucket, notice the way that S3 is being referenced here. 
        MofsToApply:
          - !Sub "s3:${DSCBucket}:DomainJoin-${AWS::StackName}.mof"
        ServicePath:
          - default
        MofOperationMode:
          - Apply
        ComplianceType:
          - Custom:DomainJoinSample
        ModuleSourceBucketName:
          - "NONE"
        AllowPSGalleryModuleSource:
          - "True"
        RebootBehavior:
          - "AfterMof"
        UseComputerNameForReporting:
          - "False"
        EnableVerboseLogging:
          - "False"
        EnableDebugLogging:
          - "False"
Outputs:
  DomainAdmin:
    Value: !Join
      - ''
      - - !Ref 'DomainNetBIOSName'
        - \
        - !Ref 'DomainAdminUser'
    Description: Domain administrator account
  DomainMemberSGID:
    Value: !Ref 'DomainMembersSG'
    Description: Domain Member Security Group ID
  SecretsArn:
    Value: !Ref 'DomainJoinSecrets'
  DSCBucket:
    Value: !Ref 'DSCBucket'

Lab CLoud Endure

Guide