AWS CloudFormation - Separately Manage Network and Server Stacks by Cross Stacks
Tags: aws, devops, tutorial, cloudformation
Published: 2022-08-21 16:33:41

---
title: AWS CloudFormation - Separately Manage Network and Server Stacks by Cross Stacks
published: true
description: "AWS CloudFormation - Separately Manage Network and Server Stacks by Cross Stacks"
tags: aws,devops,tutorial,cloudformation
cover_image: https://d1j7zd7t38gahw.cloudfront.net/dev.to/AWS_CloudFormation_CrossStack.jpg
series: Infrastructure as Code in AWS

---
## 🗒️ Content

1. AWS CloudFormation 101
2. One key pair f
3. Hands-on Lab: Separately Manage Network and Server Stacks by Cross Stacks

---
## 1. AWS CloudFormation 101
### 1.1. Infrastructure as Code (IaC)
Treating infrastructure in the same way as developers treat code is one of the most fundamental principles of DevOps.

> **Infrastructure as code (IaC)** means provisioning, configuring and managing your infrastructure resources using code and templates. 

### 1.2. AWS CloudFormation
> AWS CloudFormation is a service speeding up cloud provisioning with infrastructure as code

AWS resources and infrastructure architecture are modeled and described in CloudFormation templates written in JSON or **YAML** (recommended) formatted text file. After that CloudFormation takes care of provisioning and configuring those resources for you.

The CloudFormation workflow for creating stacks as the below diagram
![CloudFormation workflow](https://d1.awsstatic.com/Products/product-name/diagrams/product-page-diagram_CloudFormation.ad3a4c93b4fdd3366da3da0de4fb084d89a5d761.png) 
CloudFormation Workflow (source: https://aws.amazon.com/cloudformation/)

## 2. CloudFormation Templates Structure
* Resource (mandatory): declare the AWS resources
* Parameters (optional): input custom values to template each time creating/updating a stack, using with `!Ref <parameter>`
* Mapping (optional): map a key to a specific value, using with `!FindInMap [ <map_name>, <top_key>, <second_key> ]`
* Condition (optional): control the creation of resources or outputs based on a condition
* Output (optional): return output values of stack
* Cross-stack: separate templates and refer values from different stacks, using with `Export` from one stack and `!ImportValue <export_name>` from other stacks

## 3. Hands-on Lab: Separately Manage Network and Server Stacks by Cross Stacks
Assume that we are developing a simple web application with the following architecture
- One VPC, one public subnet, one EC2 instance
- One security group opening in port 80 (for HTTP from anywhere)
![Application Architecture](https://d1j7zd7t38gahw.cloudfront.net/dev.to/AWS_CloudFormation_CrossStack.jpg)

Separating our infrastructure into 2 templates may help us manage them easier.

### 3.1. LabNetworkStack
The content of `lab_network_cfn.yml`:
```yaml
Description: This template manages network components

Parameters:
  ApplicationName:
    Description: An application name that is prefixed to resource names
    Type: String
    Default: lab
  VpcCidr:
    Description: The IP range (CIDR notation) for VPC in form x.x.x.x/16-28
    Type: String
    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]))$"
    Default: 10.192.0.0/16
  PublicSubnetCidr:
    Description: The IP range (CIDR notation) for public subnet in form x.x.x.x/16-28
    Type: String
    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]))$"
    Default: 10.192.10.0/24

Resources:
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub '${ApplicationName}-vpc'
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref Vpc
      CidrBlock: !Ref PublicSubnetCidr
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${ApplicationName}-public-subnet'
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${ApplicationName}-igw'
  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref Vpc
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref Vpc
      Tags:
        - Key: Name
          Value: !Sub '${ApplicationName}-rtb'
  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

Outputs:
  VpcId:
    Description: The VPC ID of lab application
    Value: !Ref Vpc
    Export:
      Name: !Sub '${AWS::StackName}-VpcId'
  SubnetId:
    Description: The Subnet ID of lab application
    Value: !Ref PublicSubnet
    Export:
      Name: !Sub '${AWS::StackName}-PublicSubnetId'
```

The template can be deployed with AWS CLI
```shell
aws s3 cp lab_network_cfn.yml s3://<your_bucket>
aws cloudformation create-stack --stack-name LabNetworkStack --template-url https://<your_bucket>.s3.<your_region>.amazonaws.com/lab_network_cfn.yml
```

### 3.2. LabServerStack
The content of `lab_server_cfn.yml`:
```yaml
Description: This template manages servers components

Parameters:
  ApplicationName:
    Description: An application name that is prefixed to resource names
    Type: String
    Default: lab
  Landscape:
    Type: String
    Default: develop
    AllowedValues:
    - develop
    - production
  NetworkStackName:
    Description: The name of Network CloudFormation stack
    Type: String
    Default: LabNetworkStack

Mappings:
  RegionMap:
    ap-southeast-1:
      AMI: ami-02ee763250491e04a
    ap-southeast-2:
      AMI: ami-0e040c48614ad1327

Conditions:
  IsProduction: !Equals [ !Ref Landscape, production]

Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub 'Security group for ${ApplicationName} instances'
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      VpcId: !ImportValue
        'Fn::Sub': '${NetworkStackName}-VpcId'
      Tags:
        - Key: Name
          Value: !Sub '${ApplicationName}-srg'
  Ec2Instance: 
    Type: AWS::EC2::Instance
    Properties: 
      ImageId: !FindInMap [ RegionMap, !Ref 'AWS::Region', AMI ]
      InstanceType: !If [ IsProduction, t2.small, t2.micro ] 
      KeyName: your_key_name
      SubnetId: !ImportValue
        'Fn::Sub': '${NetworkStackName}-PublicSubnetId'
      SecurityGroupIds:
      - !Ref SecurityGroup
      BlockDeviceMappings: 
      - DeviceName: "/dev/sda1"
        Ebs:
          VolumeSize: 12
          VolumeType: gp2
          Encrypted: true
      UserData:
        "Fn::Base64":
          !Sub |
            #!/bin/bash
            apt update
            apt install apache2 -y
      Tags:
        - Key: Name
          Value: !Sub '${ApplicationName}-ec2'

Outputs:
  InstanceIp:
    Description: The IP address of instance
    Value: !GetAtt Ec2Instance.PublicIp
```

The template can be deployed with AWS CLI
```shell
aws s3 cp lab_server_cfn.yml s3://<your_bucket>
aws cloudformation create-stack --stack-name LabServerStack --template-url https://<your_bucket>.s3.<your_region>.amazonaws.com/lab_server_cfn.yml
```

**Confirmation**
- Confirm the status of stacks are all `CREATE_COMPLETE`
- Confirm the default Ubuntu Apache web page `http://your_server_ip`
  ![EC2 Apache default page](https://d1j7zd7t38gahw.cloudfront.net/dev.to/CloudFormation_Ec2_DefaultPage.png)

### 3.3. Clean resources
The stacks can be deleted by consoles or AWS CLI in the following order
```shell
aws cloudformation delete-stack --stack-name LabServerStack
aws cloudformation delete-stack --stack-name LabNetworkStack
```