Search code examples
amazon-web-servicesaws-cloudformationamazon-ecsaws-application-load-balancer

Use Qa, Dev and Prod as an environement in Cloudformation


I have created this nested stack. I want to implement the same stack with {prod, dev, qa} environment. Like I want to up the same stack but it doesn't have any name conflicts with each other. I want to deploy the same stack in three different environment, What changes do I have to make to achieve it

Root:

---
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  bucketname:
    Type: String
    Description: Path to the bucket
    Default: webserver
  bucketpath:
    Type: String
    Description: Path to the bucket
    Default: /env #/mysql
  Env:
    Type: String
    Description: Select the appropriate environment
    AllowedValues:
      - dev
      - test
      - uat
      - prod
  Cidr:
    Type: String
    Description: Cidr for vpc
  
  Publicsubnet1:
    Type: String
    Description: public subnet 1

  Publicsubnet2:
    Type: String
    Description: public subnet 2
  
  Privatesubnet1:
    Type: String
    Description: Private subnet 1

  Privatesubnet2:
    Type: String
    Description: Private subnet 2


Resources:
      Vpcstack:
        Type: AWS::CloudFormation::Stack
        Properties:
          TemplateURL: !Sub "https://${bucketname}.s3.us-east-2.amazonaws.com${bucketpath}/vpc.yml"
          Parameters:  
            Env: Ref: Env
            Cidr: !Ref Cidr
            Publicsubnet1: !Ref Publicsubnet1
            Publicsubnet2: !Ref Publicsubnet2
            Privatesubnet1: !Ref Privatesubnet1
            Privatesubnet2: !Ref Privatesubnet2  

Vpc:

---
    AWSTemplateFormatVersion: 2010-09-09
    Parameters:
      Cidr:
        Type: String
        Description: Cidr for vpc
      
      Publicsubnet1:
        Type: String
        Description: public subnet 1
    
      Publicsubnet2:
        Type: String
        Description: public subnet 2
      
      Privatesubnet1:
        Type: String
        Description: Private subnet 1
    
      Privatesubnet2:
        Type: String
        Description: Private subnet 2
      
      Env:
        Type: String
        Description: Select the appropriate environment
    
    Resources:
    
      VPC:
        Type: AWS::EC2::VPC
        Properties:
          CidrBlock: !Ref Cidr
          EnableDnsSupport: true
          EnableDnsHostnames: true
          InstanceTenancy: default
      InternetGateway:
        Type: AWS::EC2::InternetGateway
      VPCGatewayAttachment:
        Type: AWS::EC2::VPCGatewayAttachment
        Properties:
          VpcId: !Ref VPC
          InternetGatewayId: !Ref InternetGateway
      SubnetA:
        Type: AWS::EC2::Subnet
        Properties:
          AvailabilityZone: us-east-2a
          VpcId: !Ref VPC
          CidrBlock: !Ref Publicsubnet1
          MapPublicIpOnLaunch: true
      SubnetB:
        Type: AWS::EC2::Subnet
        Properties:
          AvailabilityZone: us-east-2b
          VpcId: !Ref VPC
          CidrBlock: !Ref Publicsubnet2
          MapPublicIpOnLaunch: true
      SubnetC:
        Type: AWS::EC2::Subnet
        Properties:
          AvailabilityZone: us-east-2a
          VpcId: !Ref VPC
          CidrBlock: !Ref Privatesubnet1
          MapPublicIpOnLaunch: false
      SubnetD:
        Type: AWS::EC2::Subnet
        Properties:
          AvailabilityZone: us-east-2b
          VpcId: !Ref VPC
          CidrBlock: !Ref Privatesubnet2
          MapPublicIpOnLaunch: false
      RouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId: !Ref VPC
      RouteTable2:
        Type: AWS::EC2::RouteTable
        Properties:
          VpcId: !Ref VPC
      InternetRoute:
        Type: AWS::EC2::Route
        DependsOn: VPCGatewayAttachment
        Properties:
          DestinationCidrBlock: 0.0.0.0/0
          GatewayId: !Ref InternetGateway
          RouteTableId: !Ref RouteTable
      SubnetARouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          RouteTableId: !Ref RouteTable
          SubnetId: !Ref SubnetA
      SubnetBRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          RouteTableId: !Ref RouteTable
          SubnetId: !Ref SubnetB
      SubnetCRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          RouteTableId: !Ref RouteTable2
          SubnetId: !Ref SubnetC
    
      SubnetDRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
          RouteTableId: !Ref RouteTable2
          SubnetId: !Ref SubnetD
      SecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
          GroupName: "Internet Group"
          GroupDescription: "SSH traffic in, all traffic out."
          VpcId: !Ref VPC
          SecurityGroupIngress:
            - IpProtocol: tcp
              FromPort: "22"
              ToPort: "22"
              CidrIp: 0.0.0.0/0
          SecurityGroupEgress:
            - IpProtocol: -1
              CidrIp: 0.0.0.0/0
      NAT:
        Type: AWS::EC2::NatGateway
        Properties:
          AllocationId:
            Fn::GetAtt:
              - EIP
              - AllocationId
          SubnetId:
            Ref: SubnetA
          Tags:
            - Key: Name
              Value: !Sub "nat-${Env}"
      EIP:
        DependsOn: VPCGatewayAttachment
        Type: AWS::EC2::EIP
        Properties:
          Domain: VPC
      Route:
        Type: AWS::EC2::Route
        Properties:
          RouteTableId:
            Ref: RouteTable2
          DestinationCidrBlock: 0.0.0.0/0
          NatGatewayId:
            Ref: NAT
    Outputs:
      VpcID:
        Description: VPC id
        Value: !Ref VPC
        Export:
          Name: "VpcID"
      SubnetA:
        Description: public subnet
        Value: !Ref SubnetA
        Export:
          Name: "SubnetA"
      SubnetB:
        Description: public subnet 2
        Value: !Ref SubnetB
        Export:
          Name: "SubnetB"
      SubnetC:
        Description: priavte subnet
        Value: !Ref SubnetC
        Export:
          Name: "SubnetC"
      SubnetD:
        Description: private subnet 2
        Value: !Ref SubnetD
        Export:
          Name: "SubnetD"
    

Solution

  • You can specify different names for your top-level stacks by adding the environment to the top-level stack name. You do this at the time of stack creation, via the console or programmatically.

    Then when each top-level environment-specific stack runs it will create the necessary nested stacks without name conflicts. You won't be able to control the stack names for the nested stacks, but you can get the name using outputs.

    See the following:

    You can add output values from a nested stack within the containing template. You use the GetAtt function with the nested stack's logical name and the name of the output value in the nested stack in the format Outputs.NestedStackOutputName.

    If you need to use different resource values for the different environments, then you can use mappings to specify the settings that correspond to the selected environment. Here is an example of mappings:

    Mappings:
      EnvTypeMap:
        prod:
          vpc: vpc-a6842gb0
          subnet: subnet-hjk23553
        dev:
          vpc: vpc-b7742gb0
          subnet: subnet-abc23553
        qa:
          vpc: vpc-c2542gb0
          subnet: subnet-uio23553
    

    Then to reference one of these mapping values you would do so like this:

    VpcId: 
      Fn::FindInMap:
        - EnvTypeMap
        - Ref: Env
        - vpc