Use nested CF template for Network Stack

Extract common part of the network stack creation to a nested template.
This change simplify code changes helps avoid code repetition.

Feature: Issue 13151
Change-Id: I118404d6ca480f98b47532543d78aa0c74405916
diff --git a/Makefile.common b/Makefile.common
index e5bf82a..5d5e7c8 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -9,3 +9,4 @@
 upload-common-templates:
 	export AWS_PAGER=; aws s3api head-bucket --bucket $(TEMPLATE_BUCKET_NAME) 2>/dev/null || aws s3api create-bucket --bucket $(TEMPLATE_BUCKET_NAME)
 	aws s3 cp ../common-templates/cf-gerrit-task-execution-role.yml s3://$(TEMPLATE_BUCKET_NAME)/
+	aws s3 cp ../common-templates/cf-gerrit-network-stack.yml s3://$(TEMPLATE_BUCKET_NAME)/
diff --git a/common-templates/cf-gerrit-network-stack.yml b/common-templates/cf-gerrit-network-stack.yml
new file mode 100644
index 0000000..4ea867b
--- /dev/null
+++ b/common-templates/cf-gerrit-network-stack.yml
@@ -0,0 +1,68 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Description: Deploy a service into an ECS cluster behind a public load balancer.
+
+Mappings:
+  # Hard values for the subnet masks. These masks define
+  # the range of internal IP addresses that can be assigned.
+  # The VPC can have all IP's from 10.0.0.0 to 10.0.255.255
+  # There is the subnet which cover the ranges:
+  #
+  # 10.0.0.0 - 10.0.0.255
+  SubnetConfig:
+    VPC:
+      CIDR: '10.0.0.0/16'
+    PublicOne:
+      CIDR: '10.0.0.0/24'
+Resources:
+  VPC:
+    Type: AWS::EC2::VPC
+    Properties:
+      EnableDnsSupport: true
+      EnableDnsHostnames: true
+      CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
+
+  # Public subnets, where containers can have public IP addresses
+  PublicSubnetOne:
+    Type: AWS::EC2::Subnet
+    Properties:
+      AvailabilityZone:
+         Fn::Select:
+         - 0
+         - Fn::GetAZs: {Ref: 'AWS::Region'}
+      VpcId: !Ref 'VPC'
+      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
+      MapPublicIpOnLaunch: true
+
+  # Setup networking resources for the public subnets. Containers
+  # in the public subnets have public IP addresses and the routing table
+  # sends network traffic via the internet gateway.
+  InternetGateway:
+    Type: AWS::EC2::InternetGateway
+  GatewayAttachement:
+    Type: AWS::EC2::VPCGatewayAttachment
+    Properties:
+      VpcId: !Ref 'VPC'
+      InternetGatewayId: !Ref 'InternetGateway'
+  PublicRouteTable:
+    Type: AWS::EC2::RouteTable
+    Properties:
+      VpcId: !Ref 'VPC'
+  PublicRoute:
+    Type: AWS::EC2::Route
+    DependsOn: GatewayAttachement
+    Properties:
+      RouteTableId: !Ref 'PublicRouteTable'
+      DestinationCidrBlock: '0.0.0.0/0'
+      GatewayId: !Ref 'InternetGateway'
+  PublicSubnetOneRouteTableAssociation:
+    Type: AWS::EC2::SubnetRouteTableAssociation
+    Properties:
+      SubnetId: !Ref PublicSubnetOne
+      RouteTableId: !Ref PublicRouteTable
+Outputs:
+  VPCRef:
+    Value: !Ref VPC
+  PublicSubnetOneRef:
+    Value: !Ref PublicSubnetOne
+  PublicOneCIDR:
+    Value: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
diff --git a/dual-master/cf-cluster.yml b/dual-master/cf-cluster.yml
index 8e49ead..3a70b84 100644
--- a/dual-master/cf-cluster.yml
+++ b/dual-master/cf-cluster.yml
@@ -33,65 +33,7 @@
       Description: An environment name used to build the log stream names
       Type: String
       Default: test
-Mappings:
-  # Hard values for the subnet masks. These masks define
-  # the range of internal IP addresses that can be assigned.
-  # The VPC can have all IP's from 10.0.0.0 to 10.0.255.255
-  # There is the subnet which cover the ranges:
-  #
-  # 10.0.0.0 - 10.0.0.255
-  SubnetConfig:
-    VPC:
-      CIDR: '10.0.0.0/16'
-    PublicOne:
-      CIDR: '10.0.0.0/24'
 Resources:
-  VPC:
-    Type: AWS::EC2::VPC
-    Properties:
-      EnableDnsSupport: true
-      EnableDnsHostnames: true
-      CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
-
-  # Public subnets, where containers can have public IP addresses
-  PublicSubnetOne:
-    Type: AWS::EC2::Subnet
-    Properties:
-      AvailabilityZone:
-         Fn::Select:
-         - 0
-         - Fn::GetAZs: {Ref: 'AWS::Region'}
-      VpcId: !Ref 'VPC'
-      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
-      MapPublicIpOnLaunch: true
-
-  # Setup networking resources for the public subnets. Containers
-  # in the public subnets have public IP addresses and the routing table
-  # sends network traffic via the internet gateway.
-  InternetGateway:
-    Type: AWS::EC2::InternetGateway
-  GatewayAttachement:
-    Type: AWS::EC2::VPCGatewayAttachment
-    Properties:
-      VpcId: !Ref 'VPC'
-      InternetGatewayId: !Ref 'InternetGateway'
-  PublicRouteTable:
-    Type: AWS::EC2::RouteTable
-    Properties:
-      VpcId: !Ref 'VPC'
-  PublicRoute:
-    Type: AWS::EC2::Route
-    DependsOn: GatewayAttachement
-    Properties:
-      RouteTableId: !Ref 'PublicRouteTable'
-      DestinationCidrBlock: '0.0.0.0/0'
-      GatewayId: !Ref 'InternetGateway'
-  PublicSubnetOneRouteTableAssociation:
-    Type: AWS::EC2::SubnetRouteTableAssociation
-    Properties:
-      SubnetId: !Ref PublicSubnetOne
-      RouteTableId: !Ref PublicRouteTable
-
   # ECS Resources
   ECSCluster:
     Type: AWS::ECS::Cluster
@@ -100,7 +42,7 @@
     Type: AWS::EC2::SecurityGroup
     Properties:
       GroupDescription: Access to the ECS hosts that run containers
-      VpcId: !Ref 'VPC'
+      VpcId: !GetAtt ECSTaskNetworkStack.Outputs.VPCRef
       SecurityGroupIngress:
           # Allow access to NLB from anywhere on the internet
           - CidrIp: 0.0.0.0/0
@@ -118,7 +60,7 @@
     Type: AWS::AutoScaling::AutoScalingGroup
     Properties:
       VPCZoneIdentifier:
-        - !Ref PublicSubnetOne
+        - !GetAtt ECSTaskNetworkStack.Outputs.PublicSubnetOneRef
       LaunchConfigurationName: !Ref 'ContainerInstances'
       MinSize: '1'
       MaxSize: !Ref 'MaxSize'
@@ -289,19 +231,24 @@
     Type: AWS::EFS::MountTarget
     Properties:
       FileSystemId: !Ref FileSystem
-      SubnetId: !Ref PublicSubnetOne
+      SubnetId: !GetAtt ECSTaskNetworkStack.Outputs.PublicSubnetOneRef
       SecurityGroups:
         - !Ref MountTargetSecurityGroup
   MountTargetSecurityGroup:
     Type: AWS::EC2::SecurityGroup
     Properties:
-      VpcId: !Ref 'VPC'
+      VpcId: !GetAtt ECSTaskNetworkStack.Outputs.VPCRef
       GroupDescription: "Security group for mount target"
       SecurityGroupIngress:
         - IpProtocol: TCP
           FromPort: 2049
           ToPort: 2049
-          CidrIp: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
+          CidrIp: !GetAtt ECSTaskNetworkStack.Outputs.PublicOneCIDR
+  ECSTaskNetworkStack:
+    Type: AWS::CloudFormation::Stack
+    Properties:
+      TemplateURL: https://aws-gerrit-cf-templates.s3.amazonaws.com/cf-gerrit-network-stack.yml
+      TimeoutInMinutes: '25'
 
 Outputs:
   ClusterName:
@@ -311,11 +258,12 @@
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'ClusterName' ] ]
   VPCId:
     Description: The ID of the VPC that this stack is deployed in
-    Value: !Ref 'VPC'
+    Value: !GetAtt ECSTaskNetworkStack.Outputs.VPCRef
     Export:
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'VPCId' ] ]
   PublicSubnetOne:
     Description: Public subnet one
-    Value: !Ref 'PublicSubnetOne'
+    Value: !GetAtt ECSTaskNetworkStack.Outputs.PublicSubnetOneRef
     Export:
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'PublicSubnetOne' ] ]
+
diff --git a/master-slave/cf-cluster.yml b/master-slave/cf-cluster.yml
index 1f2e1e9..b5767a0 100644
--- a/master-slave/cf-cluster.yml
+++ b/master-slave/cf-cluster.yml
@@ -33,65 +33,7 @@
       Description: An environment name used to build the log stream names
       Type: String
       Default: test
-Mappings:
-  # Hard values for the subnet masks. These masks define
-  # the range of internal IP addresses that can be assigned.
-  # The VPC can have all IP's from 10.0.0.0 to 10.0.255.255
-  # There is the subnet which cover the ranges:
-  #
-  # 10.0.0.0 - 10.0.0.255
-  SubnetConfig:
-    VPC:
-      CIDR: '10.0.0.0/16'
-    PublicOne:
-      CIDR: '10.0.0.0/24'
 Resources:
-  VPC:
-    Type: AWS::EC2::VPC
-    Properties:
-      EnableDnsSupport: true
-      EnableDnsHostnames: true
-      CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
-
-  # Public subnets, where containers can have public IP addresses
-  PublicSubnetOne:
-    Type: AWS::EC2::Subnet
-    Properties:
-      AvailabilityZone:
-         Fn::Select:
-         - 0
-         - Fn::GetAZs: {Ref: 'AWS::Region'}
-      VpcId: !Ref 'VPC'
-      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
-      MapPublicIpOnLaunch: true
-
-  # Setup networking resources for the public subnets. Containers
-  # in the public subnets have public IP addresses and the routing table
-  # sends network traffic via the internet gateway.
-  InternetGateway:
-    Type: AWS::EC2::InternetGateway
-  GatewayAttachement:
-    Type: AWS::EC2::VPCGatewayAttachment
-    Properties:
-      VpcId: !Ref 'VPC'
-      InternetGatewayId: !Ref 'InternetGateway'
-  PublicRouteTable:
-    Type: AWS::EC2::RouteTable
-    Properties:
-      VpcId: !Ref 'VPC'
-  PublicRoute:
-    Type: AWS::EC2::Route
-    DependsOn: GatewayAttachement
-    Properties:
-      RouteTableId: !Ref 'PublicRouteTable'
-      DestinationCidrBlock: '0.0.0.0/0'
-      GatewayId: !Ref 'InternetGateway'
-  PublicSubnetOneRouteTableAssociation:
-    Type: AWS::EC2::SubnetRouteTableAssociation
-    Properties:
-      SubnetId: !Ref PublicSubnetOne
-      RouteTableId: !Ref PublicRouteTable
-
   # ECS Resources
   ECSCluster:
     Type: AWS::ECS::Cluster
@@ -247,6 +189,11 @@
               - 'ecr:BatchGetImage'
               - 'ecr:GetDownloadUrlForLayer'
             Resource: '*'
+  ECSTaskNetworkStack:
+    Type: AWS::CloudFormation::Stack
+    Properties:
+      TemplateURL: https://aws-gerrit-cf-templates.s3.amazonaws.com/cf-gerrit-network-stack.yml
+      TimeoutInMinutes: '25'
 
 Outputs:
   ClusterName:
@@ -256,11 +203,11 @@
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'ClusterName' ] ]
   VPCId:
     Description: The ID of the VPC that this stack is deployed in
-    Value: !Ref 'VPC'
+    Value: !GetAtt ECSTaskNetworkStack.Outputs.VPCRef
     Export:
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'VPCId' ] ]
   PublicSubnetOne:
     Description: Public subnet one
-    Value: !Ref 'PublicSubnetOne'
+    Value: !GetAtt ECSTaskNetworkStack.Outputs.PublicSubnetOneRef
     Export:
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'PublicSubnetOne' ] ]
diff --git a/single-master/cf-cluster.yml b/single-master/cf-cluster.yml
index 205da58..c020a07 100644
--- a/single-master/cf-cluster.yml
+++ b/single-master/cf-cluster.yml
@@ -33,65 +33,7 @@
       Description: An environment name that will be prefixed to resource names
       Type: String
       Default: test
-Mappings:
-  # Hard values for the subnet masks. These masks define
-  # the range of internal IP addresses that can be assigned.
-  # The VPC can have all IP's from 10.0.0.0 to 10.0.255.255
-  # There is the subnet which cover the ranges:
-  #
-  # 10.0.0.0 - 10.0.0.255
-  SubnetConfig:
-    VPC:
-      CIDR: '10.0.0.0/16'
-    PublicOne:
-      CIDR: '10.0.0.0/24'
 Resources:
-  VPC:
-    Type: AWS::EC2::VPC
-    Properties:
-      EnableDnsSupport: true
-      EnableDnsHostnames: true
-      CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
-
-  # Public subnets, where containers can have public IP addresses
-  PublicSubnetOne:
-    Type: AWS::EC2::Subnet
-    Properties:
-      AvailabilityZone:
-         Fn::Select:
-         - 0
-         - Fn::GetAZs: {Ref: 'AWS::Region'}
-      VpcId: !Ref 'VPC'
-      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
-      MapPublicIpOnLaunch: true
-
-  # Setup networking resources for the public subnets. Containers
-  # in the public subnets have public IP addresses and the routing table
-  # sends network traffic via the internet gateway.
-  InternetGateway:
-    Type: AWS::EC2::InternetGateway
-  GatewayAttachement:
-    Type: AWS::EC2::VPCGatewayAttachment
-    Properties:
-      VpcId: !Ref 'VPC'
-      InternetGatewayId: !Ref 'InternetGateway'
-  PublicRouteTable:
-    Type: AWS::EC2::RouteTable
-    Properties:
-      VpcId: !Ref 'VPC'
-  PublicRoute:
-    Type: AWS::EC2::Route
-    DependsOn: GatewayAttachement
-    Properties:
-      RouteTableId: !Ref 'PublicRouteTable'
-      DestinationCidrBlock: '0.0.0.0/0'
-      GatewayId: !Ref 'InternetGateway'
-  PublicSubnetOneRouteTableAssociation:
-    Type: AWS::EC2::SubnetRouteTableAssociation
-    Properties:
-      SubnetId: !Ref PublicSubnetOne
-      RouteTableId: !Ref PublicRouteTable
-
   # ECS Resources
   ECSCluster:
     Type: AWS::ECS::Cluster
@@ -100,7 +42,7 @@
     Type: AWS::EC2::SecurityGroup
     Properties:
       GroupDescription: Access to the ECS hosts that run containers
-      VpcId: !Ref 'VPC'
+      VpcId: !GetAtt ECSTaskNetworkStack.Outputs.VPCRef
       SecurityGroupIngress:
           # Allow access to NLB from anywhere on the internet
           - CidrIp: 0.0.0.0/0
@@ -118,7 +60,7 @@
     Type: AWS::AutoScaling::AutoScalingGroup
     Properties:
       VPCZoneIdentifier:
-        - !Ref PublicSubnetOne
+        - !GetAtt ECSTaskNetworkStack.Outputs.PublicSubnetOneRef
       LaunchConfigurationName: !Ref 'ContainerInstances'
       MinSize: '1'
       MaxSize: !Ref 'MaxSize'
@@ -223,6 +165,12 @@
               - 'ecr:GetDownloadUrlForLayer'
             Resource: '*'
 
+  ECSTaskNetworkStack:
+    Type: AWS::CloudFormation::Stack
+    Properties:
+      TemplateURL: https://aws-gerrit-cf-templates.s3.amazonaws.com/cf-gerrit-network-stack.yml
+      TimeoutInMinutes: '25'
+
 Outputs:
   ClusterName:
     Description: The name of the ECS cluster
@@ -231,11 +179,11 @@
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'ClusterName' ] ]
   VPCId:
     Description: The ID of the VPC that this stack is deployed in
-    Value: !Ref 'VPC'
+    Value: !GetAtt ECSTaskNetworkStack.Outputs.VPCRef
     Export:
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'VPCId' ] ]
   PublicSubnetOne:
     Description: Public subnet one
-    Value: !Ref 'PublicSubnetOne'
+    Value: !GetAtt ECSTaskNetworkStack.Outputs.PublicSubnetOneRef
     Export:
       Name: !Join [ ':', [ !Ref 'AWS::StackName', 'PublicSubnetOne' ] ]