Skip to content

Creating a Virtual Private Cloud

Creating an Amazon VPC

Creating A VPC with Terraform is fairly straight forward. A minimal functional example configuration is just:

resource "aws_vpc" "main" {
  cidr_block       = "10.0.0.0/16"
  tags             = var.common_tags
}

Clone the terraform scaffold into aws_vpc.

$ scaffold aws_vpc
...

Add this file aws_vpc.main.tf to the a scaffold in aws_vpc.

The only variable set, is your CIDR block, 10.0.0.0/16. 10.0.0.0/16 is a large range of ip addresses, it is more than adequate at 65536 IP addresses. https://www.ipaddressguide.com/cidr.

Add the following environmental variables to your shell, replacing < yourregion > and either specify a profile or leave it as default.

AWS_REGION="<yourregion>"
AWS_PROFILE=default

Check the configuration with

$ terraform init
...

and then open your shell at ./aws_vpc:

$ terraform plan
...
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:
 + aws_vpc.main
      id:                               <computed>
      arn:                              <computed>
      assign_generated_ipv6_cidr_block: "false"
      cidr_block:                       "10.0.0.0/16"
      default_network_acl_id:           <computed>
      default_route_table_id:           <computed>
      default_security_group_id:        <computed>
      dhcp_options_id:                  <computed>
      enable_classiclink:               <computed>
      enable_classiclink_dns_support:   <computed>
      enable_dns_hostnames:             <computed>
      enable_dns_support:               "true"
      instance_tenancy:                 "default"
      ipv6_association_id:              <computed>
      ipv6_cidr_block:                  <computed>
      main_route_table_id:              <computed>
      owner_id:                         <computed>
      tags.%:                           "1"
      tags.createdby:                   "Terraform"

Plan: 1 to add, 0 to change, 0 to destroy.

If your plan looks like above then you can process with terraform apply, by default this now repeats a plan first and then asks you if you want to proceed:

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_vpc.main
      id:                               <computed>
      arn:                              <computed>
      assign_generated_ipv6_cidr_block: "false"
      cidr_block:                       "10.0.0.0/16"
      default_network_acl_id:           <computed>
      default_route_table_id:           <computed>
      default_security_group_id:        <computed>
      dhcp_options_id:                  <computed>
      enable_classiclink:               <computed>
      enable_classiclink_dns_support:   <computed>
      enable_dns_hostnames:             <computed>
      enable_dns_support:               "true"
      instance_tenancy:                 "dedicated"
      ipv6_association_id:              <computed>
      ipv6_cidr_block:                  <computed>
      main_route_table_id:              <computed>
      owner_id:                         <computed>
      tags.%:                           "1"
      tags.Name:                        "Terraform"


Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:

Enter yes if you want to make the VPC.

  Enter a value: yes

aws_vpc.main: Creating...
  arn:                              "" => "<computed>"
  assign_generated_ipv6_cidr_block: "" => "false"
  cidr_block:                       "" => "10.0.0.0/16"
  default_network_acl_id:           "" => "<computed>"
  default_route_table_id:           "" => "<computed>"
  default_security_group_id:        "" => "<computed>"
  dhcp_options_id:                  "" => "<computed>"
  enable_classiclink:               "" => "<computed>"
  enable_classiclink_dns_support:   "" => "<computed>"
  enable_dns_hostnames:             "" => "<computed>"
  enable_dns_support:               "" => "true"
  instance_tenancy:                 "" => "dedicated"
  ipv6_association_id:              "" => "<computed>"
  ipv6_cidr_block:                  "" => "<computed>"
  main_route_table_id:              "" => "<computed>"
  owner_id:                         "" => "<computed>"
  tags.%:                           "" => "1"
  tags.Name:                        "" => "Terraform"
aws_vpc.main: Creation complete after 4s (ID: vpc-0382578ed3cd51dcc)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Now you have a VPC, and not much else.

Open up the AWS console vpc page:

https://eu-west-1.console.aws.amazon.com/vpc/home?region=eu-west-1#vpcs:sort=VpcId

Add key createby with a value - your full name.

Now rerun and verify the desired state.

$ terraform plan
aws_vpc.main: Refreshing state... (ID: vpc-0382578ed3cd51dcc)

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  ~ aws_vpc.main
      tags.%:         "2" => "1"
      tags.createdby: "JamesWoolfenden" => ""


Plan: 0 to add, 1 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

The plan tells us that there is 1 more tags on the infrastructure than in our TF "desired state". We have a choice, enforce the desired state or update the state.

$ Terraform apply
...

Eliminates the drift so there is only 1 tag.

Now that we are done with the example we should tidy up and remove what's provisioned. This is straight forward enough, when your finished with this VPC run:

$ terraform destroy
aws_vpc.main: Refreshing state... (ID: vpc-0382578ed3cd51dcc)

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  - aws_vpc.main


Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value:

Then enter yes

  Enter a value: yes

aws_vpc.main: Destroying... (ID: vpc-0382578ed3cd51dcc)
aws_vpc.main: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed.

Now in AWS were back to as if we hadn't started.

So to recap, we made a VPC, checked for drift, fixed the drift and then cleaned up our environment by destroying all our provisioned infrastructure.

Scaffold revisited

Along with the structure for the template a couple of other files arrived. These are useful if you are going to turn the folder into a git repository.

.gitignore

This contains a minimal gitignore with Terraform customizations.

.pre-commit-config.yaml

The is the config file to support the pre-commit framework, it can help enforce good repository security and health. This config contains rules to protect your secrets.

This is this repos pre-commit configuration.

---
# yamllint disable rule:indentation
repos:
  - repo: git://github.com/pre-commit/pre-commit-hooks
    rev: v2.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-added-large-files
  - repo: git://github.com/Lucas-C/pre-commit-hooks
    rev: v1.1.7
    hooks:
      - id: forbid-tabs
        exclude_types: [python, javascript, dtd, markdown, makefile]
        exclude: binary|\.bin$
  - repo: git://github.com/kintoandar/pre-commit.git
    rev: v2.1.0
    hooks:
      - id: terraform_fmt
  - repo: git://github.com/pre-commit/pre-commit-hooks
    rev: v2.4.0
    hooks:
      - id: detect-aws-credentials
      - id: detect-private-key
  - repo: https://github.com/detailyang/pre-commit-shell
    rev: 1.0.5
    hooks:
      - id: shell-lint
  - repo: git://github.com/igorshubovych/markdownlint-cli
    rev: v0.19.0
    hooks:
      - id: markdownlint
  - repo: git://github.com/adrienverge/yamllint
    rev: v1.18.0
    hooks:
      - id: yamllint
        name: yamllint
        description: This hook runs yamllint.
        entry: yamllint
        language: python
        types: [file, yaml]
  - repo: git://github.com/prettier/prettier
    rev: 1.18.2
    hooks:
      - id: prettier

Makefile

There is also is a default Makefile for running Terraform

#Makefile

.PHONY: all

all: init plan build

init:
  rm -rf .terraform/modules/
  terraform init -reconfigure

plan: init
  terraform plan -refresh=true

build: init
  terraform apply -auto-approve

check: init
  terraform plan -detailed-exitcode

destroy: init
  terraform destroy -force

docs:
  terraform-docs md . > README.md

valid:
  tflint
  terraform fmt -check=true -diff=true

If you plan to turn this folder into a repo you need to install the config (at the root of the repo) with:

$ pre-commit install
pre-commit installed at .\.git\hooks\pre-commit

Tf-scaffold https://github.com/JamesWoolfenden/tf-scaffold

Help documents for Terraform AWS Virtual Private Cloud (VPC) object are here <https://www.terraform.io/docs/providers/aws/r/vpc.html>

VPC designs <https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenarios.html>