DEV Community

maso
maso

Posted on

DAY7 -IaC

Overview

Today, I’ll build a basic VPC baseline, the same as Day1, using CloudFormation. CloudFormation makes it easier to create and update resources.
I'll deploy two CloudFormation stacks to learn cross-stack references. The network stack contains network resources (VPC, subnets, IGW...) and the app stack contains app resources (EC2, SG...).

Hands-on

1. Create YAML templates

Create two YAML files using the templates below.

"day7-network.yaml"
AWSTemplateFormatVersion: '2010-09-09'
Description: Day7 Network Stack - VPC baseline with Exports (2AZ public/private, IGW, S3 Gateway Endpoint)

 # Create Variables
Parameters:
  VpcCidr:
    Type: String
    Default: 10.0.0.0/16
  Az1:
    Type: AWS::EC2::AvailabilityZone::Name
  Az2:
    Type: AWS::EC2::AvailabilityZone::Name

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: hs-day7-vpc

  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: hs-day7-igw

  AttachIgw:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref IGW

  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref Az1
      CidrBlock: 10.0.0.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: hs-day7-public-a

  PublicSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Ref Az2
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: hs-day7-public-b

  PublicRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: hs-day7-rt-public

  PublicRouteDefault:
    Type: AWS::EC2::Route
    DependsOn: AttachIgw
    Properties:
      RouteTableId: !Ref PublicRT
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW

  AssocPublicA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRT

  AssocPublicB:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetB
      RouteTableId: !Ref PublicRT

 # Output resource information for reference from the stack
Outputs:
  VpcId:
    Value: !Ref VPC
    Export:
      Name: !Sub "${AWS::StackName}-VpcId"

  PublicSubnetAId:
    Value: !Ref PublicSubnetA
    Export:
      Name: !Sub "${AWS::StackName}-PublicSubnetAId"

  PublicSubnetBId:
    Value: !Ref PublicSubnetB
    Export:
      Name: !Sub "${AWS::StackName}-PublicSubnetBId"

  PublicRouteTableId:
    Value: !Ref PublicRT
    Export:
      Name: !Sub "${AWS::StackName}-PublicRouteTableId"

Enter fullscreen mode Exit fullscreen mode

"day7-app.yaml"
AWSTemplateFormatVersion: '2010-09-09'
Description: Day7 App Stack - Create EC2 in imported VPC/Subnet (SSM enabled)

Parameters:
  NetworkStackName:
    Type: String
    Description: Name of the Network stack that exports VPC/Subnets (e.g., hs-day7-network)
  InstanceType:
    Type: String
    Default: t3.micro

Resources:
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "hs-day7-ec2-ssm-role-${AWS::StackName}"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref Ec2Role

 # Create SG for EC2 instance using VPC ID imported by the network stack
  Ec2Sg:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: hs-day7-ec2-sg (no inbound, all outbound)
      VpcId: !ImportValue
        Fn::Sub: "${NetworkStackName}-VpcId"
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

 # Create an EC2 instance using subnet ID imported by the network stack
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Sub "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}"
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref Ec2InstanceProfile
      SubnetId: !ImportValue
        Fn::Sub: "${NetworkStackName}-PublicSubnetAId"
      SecurityGroupIds:
        - !Ref Ec2Sg
      Tags:
        - Key: Name
          Value: hs-day7-app-ec2

 # Output the instance id to review the instance.
Outputs:
  InstanceId:
    Value: !Ref Ec2Instance
Enter fullscreen mode Exit fullscreen mode

2. Create CloudFormation stack

1. Network stack

CloudFormation → Create stack

Upload "day7-network.yaml".

Choose two Availability Zones (AZs).

You can review the resources created by this stack in the "Resources" tab. (Network resources like VPC, subnets, IGW...)

2. App stack

CloudFormation → Create stack

Upload "day7-app.yaml".
NetworkStackName = the network stack name created in the previous step.

You can review the resources created by this stack in the "Resources" tab. (App resources like EC2, SG, IAMrole...)

3. Functionality Verification

Systems Manager → Session Manager → Start session
If you can start a Session Manager session to the EC2 instance, you’ve successfully created both stacks.

Update "day7-app.yaml" to add a tag to the EC2 instance.
※"Environment : dev" tag!

"day7-app.yaml"
AWSTemplateFormatVersion: '2010-09-09'
Description: Day7 App Stack - Create EC2 in imported VPC/Subnet (SSM enabled)

Parameters:
  NetworkStackName:
    Type: String
    Description: Name of the Network stack that exports VPC/Subnets (e.g., hs-day7-network)
  InstanceType:
    Type: String
    Default: t3.micro

Resources:
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "hs-day7-ec2-ssm-role-${AWS::StackName}"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref Ec2Role

 # Create SG for EC2 instance using VPC ID imported by the network stack
  Ec2Sg:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: hs-day7-ec2-sg (no inbound, all outbound)
      VpcId: !ImportValue
        Fn::Sub: "${NetworkStackName}-VpcId"
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0

 # Create an EC2 instance using subnet ID imported by the network stack
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Sub "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64}}"
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref Ec2InstanceProfile
      SubnetId: !ImportValue
        Fn::Sub: "${NetworkStackName}-PublicSubnetAId"
      SecurityGroupIds:
        - !Ref Ec2Sg
      Tags:
        - Key: Name
          Value: hs-day7-app-ec2
        - Key: Environment
          Value: dev

 # Output the instance id to review the instance.
Outputs:
  InstanceId:
    Value: !Ref Ec2Instance
Enter fullscreen mode Exit fullscreen mode

CloudFormation → Stacks → the app stack → Create a change set → Update stack → Replace template → Create change set

You can review the differences in the "Resource changes" tab.
※In this time, there are differences with tag settings.
Execute change set.

Execute change set, and the tag is added.

Tidying up

  • Delete the app stack
  • Delete the network stack

For test

Key exam points related to today's services.

CloudFormation

  • Cross-stack references: export values from one stack (Outputs/Export) and import them in another stack (ImportValue). Export names must be unique within the same account and region.
  • Change set is used when you change the template. Depending on the changes made, either in-place update or replacement are performed. (※In case of replacement, the resource is recreated. Some parameters like EC2 instance ID are changed.)

It's a last hands-on lab, see you soon in the Day8!
I'll summarize the key points of the services not covered in hands-on sessions.

Top comments (0)