How to launch an EC2 instance in an existing VPC using CloudFormation

How to launch an EC2 instance in an existing VPC using CloudFormation

Launch an EC2 instance in an existing VPC using CloudFormation

In this post, I will help you launch an EC2 instance in an existing VPC using CloudFormation. I will also share the template in YAML as well as JSON for your convenience.

Don’t want to miss any posts from us? join us on our Facebook group, and follow us on Facebook, Twitter, LinkedIn, and Instagram. You can also subscribe to our newsletter below to not miss any updates from us.

Prerequisite

To be honest, I have seen many people asking this question on various online forums.

There is so much confusion around launching an EC2 in a VPC. And confusion is so obvious, especially for beginners. So why not start with the confusion itself? 🙂

Why Confusion?

Well, If you have launched an EC2 instance from the AWS EC2 console. You might remember that, In the network, we get to select a VPC and then subnet in the VPC in which you would like to launch the instance.

However, there is no VPC or VpcId parameter for AWS::EC2::Instance resource in CloudFormation.

You might wonder, How do we create an instance in a VPC then?

Don’t worry It’s simple.

SubnetId represents a VPC when creating an EC2 instance using Cloud Formation. Therefore need to specify the SubnetId parameter to launch the instance in VPC.

What’s even more confusing?

There are two places where SubnetId can be specified in a AWS::EC2::Instance resource.

  1. On Instance Level
  2. On the Network Interface level

Let’s understand with an example for each one of these.

Instance Level SubnetId in an AWS::EC2::Instance resource

  DemoInstance:
    Type: 'AWS::EC2::Instance'
    Properties: 
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      AvailabilityZone: !Ref AvailabilityZone
      KeyName: !Ref KeyName
      SecurityGroupIds: 
        - !Ref DemoSecurityGroup
      SubnetId: !Ref SubnetId

or

Network Interface Level SubnetId in an AWS::EC2::Instance resource

  DemoInstance:
    Type: 'AWS::EC2::Instance'
    Properties: 
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      AvailabilityZone: !Ref AvailabilityZone
      KeyName: !Ref KeyName
      NetworkInterfaces:
        - DeviceIndex: 0
          AssociatePublicIpAddress: true
          DeleteOnTermination: true
          SubnetId: !Ref SubnetId
          GroupSet: 
            - !Ref DemoSecurityGroup

Where to specify SubnetId?

Have you got the requirement to attach a network interface to your instance?

Yes? -> Use SubnetId on the interface level

No? -> Use SubnetId on the instance level

Please note that only one out of two can be specified. If you try to use SubnetId on the instance level as well as the network interface level. You will get the below error.

Network interfaces and an instance-level subnet ID may not be specified on the same request (Service: AmazonEC2; Status Code: 400; Error Code: InvalidParameterCombination)

The same applies to security groups.

  • Using Network Interface -> Use GroupSet parameter on the network interface level
  • Not using Network Interface -> Use SecurityGroupIds parameter on instance level

If you try to use it at both places, you will get errors like

Network interfaces and an instance-level security groups may not be specified on the same request (Service: AmazonEC2; Status Code: 400)

Don’t worry much. I am going to share a fully working template for both cases below.

Template Example to Launch an EC2 instance in an existing VPC using CloudFormation in YAML

Please note that as we discussed above, we will be having two templates here.

  1. Without Network Interface
  2. With Network Interface

Without Network Interface:

AWSTemplateFormatVersion: '2010-09-09'
Description: Template to Create an EC2 instance in a VPC
   
Parameters:

  ImageId:
    Type: String
    Description: 'Linux 2 AMI for Ireland eu-west1 Region'
    Default: 'ami-0fc970315c2d38f01'
  VpcId:
    Type: String
    Description: VPC id
    Default: vpc-012b3456f0123456f4
  SubnetId:
    Type: String
    Description: Subnet in which to launch an EC2
    Default: subnet-0123ec12fd9af123b
  AvailabilityZone:
    Type: String
    Description: Availability Zone into which instance will launch
    Default: eu-west-1c
  InstanceType:
    Type: String
    Description: Choosing  t2 micro because it is free
    Default: t2.micro
  KeyName:
    Description: SSH Keypair to login to the instance
    Type: AWS::EC2::KeyPair::KeyName
    Default: demokeypair

Resources:
  DemoInstance:
    Type: 'AWS::EC2::Instance'
    Properties: 
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      AvailabilityZone: !Ref AvailabilityZone
      KeyName: !Ref KeyName
      SecurityGroupIds: 
        - !Ref DemoSecurityGroup
      SubnetId: !Ref SubnetId

  DemoSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VpcId
      GroupDescription: SG to allow SSH access via port 22
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: '0.0.0.0/0'
      Tags:
        - Key: Name
          Value: SSH-SG

Outputs:
  DemoInstanceId:
    Description: Instance Id 
    Value: !Ref DemoInstance

With Network Interface:

AWSTemplateFormatVersion: '2010-09-09'
Description: Template to Create an EC2 instance in a VPC
   
Parameters:

  ImageId:
    Type: String
    Description: 'Linux 2 AMI for Ireland eu-west1 Region'
    Default: 'ami-0fc970315c2d38f01'
  VpcId:
    Type: String
    Description: VPC id
    Default: vpc-012b3456f0123456f4
  SubnetId:
    Type: String
    Description: Subnet in which to launch an EC2
    Default: subnet-0123ec12fd9af123b
  AvailabilityZone:
    Type: String
    Description: Availability Zone into which instance will launch
    Default: eu-west-1c
  InstanceType:
    Type: String
    Description: Choosing  t2 micro because it is free
    Default: t2.micro
  KeyName:
    Description: SSH Keypair to login to the instance
    Type: AWS::EC2::KeyPair::KeyName
    Default: demokeypair

Resources:
  DemoInstance:
    Type: 'AWS::EC2::Instance'
    Properties: 
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      AvailabilityZone: !Ref AvailabilityZone
      KeyName: !Ref KeyName
      NetworkInterfaces:
        - DeviceIndex: 0
          AssociatePublicIpAddress: true
          DeleteOnTermination: true
          SubnetId: !Ref SubnetId
          GroupSet: 
            - !Ref DemoSecurityGroup

  DemoSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VpcId
      GroupDescription: SG to allow SSH access via port 22
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: '0.0.0.0/0'
      Tags:
        - Key: Name
          Value: SSH-SG

Outputs:
  DemoInstanceId:
    Description: Instance Id 
    Value: !Ref DemoInstance

Template Example to Launch an EC2 instance in an existing VPC using CloudFormation in JSON

Same as YAML here also we have two templates. By the way, if you have got a template in YAML or JSON, you can convert in to JSON or YAML. Here is a post on How to Convert a CloudFormation Template from YAML to JSON.

Without Network Interface:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Template to Create an EC2 instance in a VPC",
    "Parameters": {
        "ImageId": {
            "Type": "String",
            "Description": "Linux 2 AMI for Ireland eu-west1 Region",
            "Default": "ami-0fc970315c2d38f01"
        },
        "VpcId": {
            "Type": "String",
            "Description": "VPC id",
            "Default": "vpc-012b3456f0123456f4"
        },
        "SubnetId": {
            "Type": "String",
            "Description": "Subnet in which to launch an EC2",
            "Default": "subnet-0123ec12fd9af123b"
        },
        "AvailabilityZone": {
            "Type": "String",
            "Description": "Availability Zone into which instance will launch",
            "Default": "eu-west-1c"
        },
        "InstanceType": {
            "Type": "String",
            "Description": "Choosing  t2 micro because it is free",
            "Default": "t2.micro"
        },
        "KeyName": {
            "Description": "SSH Keypair to login to the instance",
            "Type": "AWS::EC2::KeyPair::KeyName",
            "Default": "demokeypair"
        }
    },
    "Resources": {
        "DemoInstance": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": {
                    "Ref": "ImageId"
                },
                "InstanceType": {
                    "Ref": "InstanceType"
                },
                "AvailabilityZone": {
                    "Ref": "AvailabilityZone"
                },
                "KeyName": {
                    "Ref": "KeyName"
                },
                "SecurityGroupIds": [
                    {
                        "Ref": "DemoSecurityGroup"
                    }
                ],
                "SubnetId": {
                    "Ref": "SubnetId"
                }
            }
        },
        "DemoSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "VpcId": {
                    "Ref": "VpcId"
                },
                "GroupDescription": "SG to allow SSH access via port 22",
                "SecurityGroupIngress": [
                    {
                        "IpProtocol": "tcp",
                        "FromPort": "22",
                        "ToPort": "22",
                        "CidrIp": "0.0.0.0/0"
                    }
                ],
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "SSH-SG"
                    }
                ]
            }
        }
    },
    "Outputs": {
        "DemoInstanceId": {
            "Description": "Instance Id",
            "Value": {
                "Ref": "DemoInstance"
            }
        }
    }
}

With Network Interface:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Template to Create an EC2 instance in a VPC",
    "Parameters": {
        "ImageId": {
            "Type": "String",
            "Description": "Linux 2 AMI for Ireland eu-west1 Region",
            "Default": "ami-0fc970315c2d38f01"
        },
        "VpcId": {
            "Type": "String",
            "Description": "VPC id",
            "Default": "vpc-012b3456f0123456f4"
        },
        "SubnetId": {
            "Type": "String",
            "Description": "Subnet in which to launch an EC2",
            "Default": "subnet-0123ec12fd9af123b"
        },
        "AvailabilityZone": {
            "Type": "String",
            "Description": "Availability Zone into which instance will launch",
            "Default": "eu-west-1c"
        },
        "InstanceType": {
            "Type": "String",
            "Description": "Choosing  t2 micro because it is free",
            "Default": "t2.micro"
        },
        "KeyName": {
            "Description": "SSH Keypair to login to the instance",
            "Type": "AWS::EC2::KeyPair::KeyName",
            "Default": "demokeypair"
        }
    },
    "Resources": {
        "DemoInstance": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "ImageId": {
                    "Ref": "ImageId"
                },
                "InstanceType": {
                    "Ref": "InstanceType"
                },
                "AvailabilityZone": {
                    "Ref": "AvailabilityZone"
                },
                "KeyName": {
                    "Ref": "KeyName"
                },
                "NetworkInterfaces": [
                    {
                        "DeviceIndex": 0,
                        "AssociatePublicIpAddress": true,
                        "DeleteOnTermination": true,
                        "SubnetId": {
                            "Ref": "SubnetId"
                        },
                        "GroupSet": [
                            {
                                "Ref": "DemoSecurityGroup"
                            }
                        ]
                    }
                ]
            }
        },
        "DemoSecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "VpcId": {
                    "Ref": "VpcId"
                },
                "GroupDescription": "SG to allow SSH access via port 22",
                "SecurityGroupIngress": [
                    {
                        "IpProtocol": "tcp",
                        "FromPort": "22",
                        "ToPort": "22",
                        "CidrIp": "0.0.0.0/0"
                    }
                ],
                "Tags": [
                    {
                        "Key": "Name",
                        "Value": "SSH-SG"
                    }
                ]
            }
        }
    },
    "Outputs": {
        "DemoInstanceId": {
            "Description": "Instance Id",
            "Value": {
                "Ref": "DemoInstance"
            }
        }
    }
}

Steps to Create a Stack

Once you have got your required template from above. Change the parameter value such as SubnetId, VpcId etc. as per your requirement. Save it with .yml or .json as per the template and follow the below steps.

  • Login to AWS Management Console, navigate to CloudFormation and click on Create stack
  • Click on “Upload a template file”, upload ec2instance.yml  or ec2instance.json and click Next
  • Enter the stack name and click on Next. In the configuration, keep everything as default and click on Next.
  • In the events tab of the stack, you can view the status.
  • Once the stack is successfully created, go to the Resources tab(Right next to Event) and click on your instance resource.
  • It will take you to your instance. You can verify all the details there.
launch an ec2 instance in an existing vpc

If you like doing it via CLI, checkout my post on how to deploy a CloudFormation template in AWS using CLI.

Conclusion

Let’s sum up what we did in this post.

  • We discussed why launching an EC2 instance in a VPC becomes confusing at times
  • We also saw how to correctly create an EC2 instance in VPC
  • Finally, I shared a working template in YAML as well as JSON

I hope you found this post helpful.

Enjoyed the content?

Subscribe to our newsletter below to get awesome AWS learning materials delivered straight to your inbox.

Don’t forget to motivate me by-

  • Adding a comment below on what you liked and what can be improved.
  • Follow us on
  • Subscribe to our newsletter to get notified each time we post new content
  • Share this post with your friends

Suggested Read:

2 thoughts on “How to launch an EC2 instance in an existing VPC using CloudFormation

  1. Thanks for a great template! I appreciated information on launching EC2 instance with existing VPC. I see a lot of templates where it’s being launched with a new VPC (nothing wrong with that).

    Keep up the great work and hope to see more!

Leave a Reply

Your email address will not be published. Required fields are marked *