AWS Cloudformation - Cross Stack Learning Summary

Now that you've learned Nested Stack in the last article, look at another way to call another stack.Suppose there are different departments in a large company. To configure a series of services, we need to configure a large template file, but we can split a large template into many smaller templates by cross stack and export/import the corresponding values in the corresponding places.

Here's an example of creating two stacks, the first to create our VPC, routing table, Internet gateway, security group, and so on; the second stack calls the results of the first stack and creates a new web server based on that

Template files can be downloaded at this link
https://github.com/natonic/CloudFormation-Deep-Dive/tree/master/Labs/CrossStack

Here's our first template file

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS CloudFormation Sample Template VPC_with_PublicIPs_And_DNS: Sample template that creates a VPC with DNS and public IPs enabled. Note that you are billed for the AWS resources that you use when you create a stack from this template.",
  "Resources" : {
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "EnableDnsSupport" : "true",
        "EnableDnsHostnames" : "true",
        "CidrBlock" : "10.0.0.0/16"
      }
    },
    "PublicSubnet" : {
      "Type" : "AWS::EC2::Subnet",
      "Properties" : {
        "VpcId" : { "Ref" : "VPC" },
        "CidrBlock" : "10.0.0.0/24"
      }
    },
    "InternetGateway" : {
      "Type" : "AWS::EC2::InternetGateway"
    },
    "VPCGatewayAttachment" : {
       "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" : "VPCGatewayAttachment",
      "Properties" : {
        "RouteTableId" : { "Ref" : "PublicRouteTable" },
        "DestinationCidrBlock" : "0.0.0.0/0",
        "GatewayId" : { "Ref" : "InternetGateway" }
      }
    },
    "PublicSubnetRouteTableAssociation" : {
      "Type" : "AWS::EC2::SubnetRouteTableAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "PublicSubnet" },
        "RouteTableId" : { "Ref" : "PublicRouteTable" }
      }
    },
    "PublicSubnetNetworkAclAssociation" : {
      "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "PublicSubnet" },
        "NetworkAclId" : { "Fn::GetAtt" : ["VPC", "DefaultNetworkAcl"] }
      }
    },
    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP ingress",
        "VpcId" : { "Ref" : "VPC" },
        "SecurityGroupIngress" : [ { 
          "IpProtocol" : "tcp",
          "FromPort" : "80",  
          "ToPort" : "80",
          "CidrIp" : "0.0.0.0/0"},
          {
            "CidrIp": "0.0.0.0/0",
            "FromPort": 443,
            "IpProtocol": "tcp",
            "ToPort": 443
          },
          {
            "FromPort": 443,
            "IpProtocol": "tcp",
            "ToPort": 443
          }
         ]
      }
    }
  },
  "Outputs" : {
    "VPCId" : {
      "Description" : "VPC ID",
      "Value" :  { "Ref" : "VPC" },
      "Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-VPCID" }}
    },
    "PublicSubnet" : {
      "Description" : "The subnet ID to use for public web servers",
      "Value" :  { "Ref" : "PublicSubnet" },
      "Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-SubnetID" }}
    },
    "WebServerSecurityGroup" : {
      "Description" : "The security group ID to use for public web servers",
      "Value" :  { "Fn::GetAtt" : ["WebServerSecurityGroup", "GroupId"] },
      "Export" : { "Name" : {"Fn::Sub": "${AWS::StackName}-SecurityGroupID" }}
    }
  }
}

Below is the second template file

{
    "AWSTemplateFormatVersion" : "2010-09-09",

    "Description" : "AWS CloudFormation Cross-Stack Reference Sample Template: Demonstrates how to reference resources from a different stack. This template provisions an EC2 instance in an EC2 Security Group provisioned in a different stack. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",

    "Parameters": {
      "NetworkStackName": {
        "Description": "Name of an active CloudFormation stack that contains the networking resources, such as the subnet and security group, that will be used in this stack.",
        "Type": "String",
        "MinLength" : 1,
        "MaxLength" : 255,
        "AllowedPattern" : "^[a-zA-Z][-a-zA-Z0-9]*$",
        "Default" : "SampleNetworkCrossStack"
      }
    },

    "Mappings" : {
      "AWSRegionArch2AMI" : {
        "us-east-1"        : {"PV64" : "ami-8ff710e2", "HVM64" : "ami-f5f41398", "HVMG2" : "ami-4afd1d27"},
        "us-west-2"        : {"PV64" : "ami-eff1028f", "HVM64" : "ami-d0f506b0", "HVMG2" : "ami-ee897b8e"},
        "us-west-1"        : {"PV64" : "ami-ac85fbcc", "HVM64" : "ami-6e84fa0e", "HVMG2" : "ami-69106909"},
        "eu-west-1"        : {"PV64" : "ami-23ab2250", "HVM64" : "ami-b0ac25c3", "HVMG2" : "ami-936de5e0"},
        "eu-central-1"     : {"PV64" : "ami-27c12348", "HVM64" : "ami-d3c022bc", "HVMG2" : "ami-8e7092e1"},
        "ap-northeast-1"   : {"PV64" : "ami-26160d48", "HVM64" : "ami-29160d47", "HVMG2" : "ami-91809aff"},
        "ap-northeast-2"   : {"PV64" : "NOT_SUPPORTED", "HVM64" : "ami-cf32faa1", "HVMG2" : "NOT_SUPPORTED"},
        "ap-southeast-1"   : {"PV64" : "ami-f3dd0a90", "HVM64" : "ami-1ddc0b7e", "HVMG2" : "ami-3c30e75f"},
        "ap-southeast-2"   : {"PV64" : "ami-8f94b9ec", "HVM64" : "ami-0c95b86f", "HVMG2" : "ami-543d1137"},
        "sa-east-1"        : {"PV64" : "ami-e188018d", "HVM64" : "ami-fb890097", "HVMG2" : "NOT_SUPPORTED"},
        "cn-north-1"       : {"PV64" : "ami-77a46e1a", "HVM64" : "ami-05a66c68", "HVMG2" : "NOT_SUPPORTED"}
      }
    },

    "Resources" : {
      "WebServerInstance": {  
        "Type": "AWS::EC2::Instance",
        "Metadata" : {
          "AWS::CloudFormation::Init" : {
            "configSets" : {
              "All" : [ "ConfigureSampleApp" ]
            },

            "ConfigureSampleApp" : {
              "packages" : {
                "yum" : {
                  "httpd" : []
                }
              },

              "files" : {
                "/var/www/html/index.html" : {
                  "content" : { "Fn::Join" : ["\n", [
                    "<img src=\"https://s3.amazonaws.com/cloudformation-examples/cloudformation_graphic.png\" alt=\"AWS CloudFormation Logo\"/>",
                    "<h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>"
                  ]]},
                  "mode"    : "000644",
                  "owner"   : "root",
                  "group"   : "root"
                }
              },

              "services" : {
                "sysvinit" : {
                  "httpd"    : { "enabled" : "true", "ensureRunning" : "true" }
                }
              }
            }
          }
        },
        "Properties": {
          "InstanceType"   : "t2.micro",
          "ImageId": { "Fn::FindInMap": [ "AWSRegionArch2AMI", { "Ref": "AWS::Region" } , "HVM64" ] },
          "NetworkInterfaces" : [{
            "GroupSet"                 : [{ "Fn::ImportValue" :  {"Fn::Sub": "${NetworkStackName}-SecurityGroupID" } }],
            "AssociatePublicIpAddress" : "true",
            "DeviceIndex"              : "0",
            "DeleteOnTermination"      : "true",
            "SubnetId"                 : { "Fn::ImportValue" : {"Fn::Sub": "${NetworkStackName}-SubnetID" } }
          }],
          "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
               "#!/bin/bash -xe\n",
               "yum update -y aws-cfn-bootstrap\n",

               "# Install the files and packages from the metadata\n",
               "/opt/aws/bin/cfn-init -v ",
               "         --stack ", { "Ref" : "AWS::StackName" },
               "         --resource WebServerInstance ",
               "         --configsets All ",
               "         --region ", { "Ref" : "AWS::Region" }, "\n",

               "# Signal the status from cfn-init\n",
               "/opt/aws/bin/cfn-signal -e $? ",
               "         --stack ", { "Ref" : "AWS::StackName" },
               "         --resource WebServerInstance ",
               "         --region ", { "Ref" : "AWS::Region" }, "\n"
          ]]}}        
        },
        "CreationPolicy" : {
          "ResourceSignal" : {
            "Timeout" : "PT5M"
          }
        }
      }
    },

    "Outputs" : {
      "URL" : {
        "Description" : "URL of the sample website",
        "Value" :  { "Fn::Join" : [ "", [ "http://", { "Fn::GetAtt" : [ "WebServerInstance", "PublicDnsName" ]}]]}
      }
    }
  }

First create the first stack

Give a name networkstack

Created successfully

We created the following resources

Look at his output

Look at his export

Next, create a second stack

His argument points to the name of the first stack for import data

Created successfully

Look at the output URL

Can successfully open this test page

Finally, try deleting our networkstack again

Delete failed because of dependencies, appstack needs to import data such as vpc, sg from networkstack

If we delete the appstack and then the network stack, that's fine

The whole process is shown above.Last but not least, highlight the key points:

We export from Output of networkstack, where stackname is replaced by global variable

Similarly, we import the corresponding value in the resource section of the appstack

Tags: AWS VPC yum Web Server

Posted on Wed, 06 May 2020 13:29:51 -0400 by mukunthan