←

AWS VPC

Virtual Private Cloud (VPC)


An AWS VPC (Virtual Private Cloud) is a logically isolated virtual network dedicated to your AWS account. It mimics a traditional physical network inside a data center, giving you complete control over your IP addressing, subnets, routing, and security.

Core components

Terraform project

Objective

Provision a basic network architecture locally

  1. VPC with a Public Subnet
  2. Internet Gateway
  3. EC2 Instance

Tools

Docker

Terraform

Floci AWS

AWS CLI

mkdir floci-terraform-vpc && cd floci-terraform-vpc

Provider setup

Set env vars

export AWS_ENDPOINT_URL=http://localhost:4566
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1

Floci setup - Docker Compose

services:
  floci:
    image: floci/floci:latest
    ports:
      - "4566:4566"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./data:/app/data
    environment:
      FLOCI_STORAGE_MODE: hybrid
docker compose up -d

Add .gitignore

# Local .terraform directories
.terraform/

# Exclude local volume directory
data/

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Ignore transient lock info files created by terraform apply
.terraform.tfstate.lock.info

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc

# Optional: ignore graph output files generated by `terraform graph`
# *.dot

# Optional: ignore plan files saved before destroying Terraform configuration
# Uncomment the line below if you want to ignore planout files.
# planout

providers.tf

provider "aws" {
  access_key                  = "test"
  secret_key                  = "test"
  region                      = "us-east-1"
  skip_credentials_validation = true
  skip_requesting_account_id  = true
  skip_metadata_api_check     = true
  s3_use_path_style           = true

  endpoints {
    apigateway     = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    ec2            = "http://localhost:4566"
    es             = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    route53        = "http://localhost:4566"
    s3             = "http://localhost:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

Initialize Terraform

terraform init
Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v6.61.0...
- Installed hashicorp/aws v6.61.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Create variables

variables.tf

variable "aws_region" {
  type    = string
  default = "us-east-1"
}

variable "vpc_cidr" {
  type    = string
  default = "10.0.0.0/16"
}

variable "public_subnet_cidr" {
  type    = string
  default = "10.0.1.0/24"
}

/16 (VPC): Provides 65,536 total IP addresses (10.0.0.0 to 10.0.255.255).

/24 (Subnet): Provides 256 total IP addresses (10.0.1.0 to 10.0.1.255).

Create VPC

vpc.tf

resource "aws_vpc" "dev_vpc" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true

  tags = {
    Name = "dev-vpc"
  }
}

terraform plan and apply

terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.dev_vpc will be created
  + resource "aws_vpc" "dev_vpc" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + region                               = "us-east-1"
      + tags                                 = {
          + "Name" = "dev-vpc"
        }
      + tags_all                             = {
          + "Name" = "dev-vpc"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.
terraform apply -auto-approve 
Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.dev_vpc will be created
  + resource "aws_vpc" "dev_vpc" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + region                               = "us-east-1"
      + tags                                 = {
          + "Name" = "dev-vpc"
        }
      + tags_all                             = {
          + "Name" = "dev-vpc"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.
aws_vpc.dev_vpc: Creating...
aws_vpc.dev_vpc: Creation complete after 0s [id=vpc-9de614ab]

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

Create Internet Gateway

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.dev_vpc.id

  tags = {
    Name = "dev-igw"
  }
}
terraform plan
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_internet_gateway.igw will be created
  + resource "aws_internet_gateway" "igw" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + region   = "us-east-1"
      + tags     = {
          + "Name" = "dev-igw"
        }
      + tags_all = {
          + "Name" = "dev-igw"
        }
      + vpc_id   = "vpc-9de614ab"
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
terraform apply -auto-approve
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_internet_gateway.igw will be created
  + resource "aws_internet_gateway" "igw" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + region   = "us-east-1"
      + tags     = {
          + "Name" = "dev-igw"
        }
      + tags_all = {
          + "Name" = "dev-igw"
        }
      + vpc_id   = "vpc-9de614ab"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
aws_internet_gateway.igw: Creating...
aws_internet_gateway.igw: Creation complete after 0s [id=igw-e05f40c7]

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

Create Subnet

resource "aws_subnet" "public_subnet" {
  vpc_id     = aws_vpc.dev_vpc.id
  cidr_block = var.public_subnet_cidr

  map_public_ip_on_launch = true

  tags = {
    Name = "dev-public-subnet"
  }
}
terraform plan
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_subnet.public_subnet will be created
  + resource "aws_subnet" "public_subnet" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block                                = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = true
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + region                                         = "us-east-1"
      + tags                                           = {
          + "Name" = "dev-public-subnet"
        }
      + tags_all                                       = {
          + "Name" = "dev-public-subnet"
        }
      + vpc_id                                         = "vpc-9de614ab"
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
terraform apply -auto-approve
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_subnet.public_subnet will be created
  + resource "aws_subnet" "public_subnet" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = (known after apply)
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.0.1.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block                                = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = true
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + region                                         = "us-east-1"
      + tags                                           = {
          + "Name" = "dev-public-subnet"
        }
      + tags_all                                       = {
          + "Name" = "dev-public-subnet"
        }
      + vpc_id                                         = "vpc-9de614ab"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
aws_subnet.public_subnet: Creating...
aws_subnet.public_subnet: Still creating... [00m10s elapsed]
aws_subnet.public_subnet: Creation complete after 10s [id=subnet-adbcfd21]

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

Create Route Table and Association

resource "aws_route_table" "public_rt" {
  vpc_id = aws_vpc.dev_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "dev-public-rt"
  }
}

resource "aws_route_table_association" "public_assoc" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_rt.id
}
terraform plan
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_route_table.public_rt will be created
  + resource "aws_route_table" "public_rt" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + region           = "us-east-1"
      + route            = [
          + {
              + cidr_block                 = "0.0.0.0/0"
              + gateway_id                 = "igw-e05f40c7"
                # (12 unchanged attributes hidden)
            },
        ]
      + tags             = {
          + "Name" = "dev-public-rt"
        }
      + tags_all         = {
          + "Name" = "dev-public-rt"
        }
      + vpc_id           = "vpc-9de614ab"
    }

  # aws_route_table_association.public_assoc will be created
  + resource "aws_route_table_association" "public_assoc" {
      + id             = (known after apply)
      + region         = "us-east-1"
      + route_table_id = (known after apply)
      + subnet_id      = "subnet-adbcfd21"
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
terraform apply -auto-approve
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_route_table.public_rt will be created
  + resource "aws_route_table" "public_rt" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + region           = "us-east-1"
      + route            = [
          + {
              + cidr_block                 = "0.0.0.0/0"
              + gateway_id                 = "igw-e05f40c7"
                # (12 unchanged attributes hidden)
            },
        ]
      + tags             = {
          + "Name" = "dev-public-rt"
        }
      + tags_all         = {
          + "Name" = "dev-public-rt"
        }
      + vpc_id           = "vpc-9de614ab"
    }

  # aws_route_table_association.public_assoc will be created
  + resource "aws_route_table_association" "public_assoc" {
      + id             = (known after apply)
      + region         = "us-east-1"
      + route_table_id = (known after apply)
      + subnet_id      = "subnet-adbcfd21"
    }

Plan: 2 to add, 0 to change, 0 to destroy.
aws_route_table.public_rt: Creating...
aws_route_table.public_rt: Creation complete after 0s [id=rtb-c8bae0b5]
aws_route_table_association.public_assoc: Creating...
aws_route_table_association.public_assoc: Creation complete after 0s [id=rtbassoc-439ae214]

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

Create Security Group

ec2.tf

resource "aws_security_group" "web_sg" {
  name        = "web-sg"
  description = "Allow HTTP and SSH traffic"
  vpc_id      = aws_vpc.dev_vpc.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
terraform plan
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]
aws_route_table.public_rt: Refreshing state... [id=rtb-c8bae0b5]
aws_route_table_association.public_assoc: Refreshing state... [id=rtbassoc-439ae214]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_security_group.web_sg will be created
  + resource "aws_security_group" "web_sg" {
      + arn                    = (known after apply)
      + description            = "Allow HTTP and SSH traffic"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
                # (1 unchanged attribute hidden)
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
                # (1 unchanged attribute hidden)
            },
        ]
      + name                   = "web-sg"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + region                 = "us-east-1"
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = "vpc-9de614ab"
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take
exactly these actions if you run "terraform apply" now.
terraform apply -auto-approve
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]
aws_route_table.public_rt: Refreshing state... [id=rtb-c8bae0b5]
aws_route_table_association.public_assoc: Refreshing state... [id=rtbassoc-439ae214]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_security_group.web_sg will be created
  + resource "aws_security_group" "web_sg" {
      + arn                    = (known after apply)
      + description            = "Allow HTTP and SSH traffic"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
                # (1 unchanged attribute hidden)
            },
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 80
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 80
                # (1 unchanged attribute hidden)
            },
        ]
      + name                   = "web-sg"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + region                 = "us-east-1"
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = "vpc-9de614ab"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
aws_security_group.web_sg: Creating...
aws_security_group.web_sg: Creation complete after 0s [id=sg-3c57687c7fb5b2ca2]

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

Create EC2 Instance

resource "aws_instance" "web_server" {
  ami                    = "ami-12345678"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.public_subnet.id
  vpc_security_group_ids = [aws_security_group.web_sg.id]

  tags = {
    Name = "dev-web-server"
  }
}
terraform plan
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]
aws_security_group.web_sg: Refreshing state... [id=sg-3c57687c7fb5b2ca2]
aws_route_table.public_rt: Refreshing state... [id=rtb-c8bae0b5]
aws_route_table_association.public_assoc: Refreshing state... [id=rtbassoc-439ae214]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.web_server will be created
  + resource "aws_instance" "web_server" {
      + ami                                  = "ami-12345678"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + enable_primary_ipv6                  = (known after apply)
      + force_destroy                        = false
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_group_id                   = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + region                               = "us-east-1"
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = "subnet-adbcfd21"
      + tags                                 = {
          + "Name" = "dev-web-server"
        }
      + tags_all                             = {
          + "Name" = "dev-web-server"
        }
      + tenancy                              = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = [
          + "sg-3c57687c7fb5b2ca2",
        ]

      + capacity_reservation_specification (known after apply)

      + cpu_options (known after apply)

      + ebs_block_device (known after apply)

      + enclave_options (known after apply)

      + ephemeral_block_device (known after apply)

      + instance_market_options (known after apply)

      + maintenance_options (known after apply)

      + metadata_options (known after apply)

      + network_interface (known after apply)

      + primary_network_interface (known after apply)

      + private_dns_name_options (known after apply)

      + root_block_device (known after apply)

      + secondary_network_interface (known after apply)
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
terraform apply -auto-approve
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_security_group.web_sg: Refreshing state... [id=sg-3c57687c7fb5b2ca2]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]
aws_route_table.public_rt: Refreshing state... [id=rtb-c8bae0b5]
aws_route_table_association.public_assoc: Refreshing state... [id=rtbassoc-439ae214]

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.web_server will be created
  + resource "aws_instance" "web_server" {
      + ami                                  = "ami-12345678"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + enable_primary_ipv6                  = (known after apply)
      + force_destroy                        = false
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_lifecycle                   = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_group_id                   = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + region                               = "us-east-1"
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + spot_instance_request_id             = (known after apply)
      + subnet_id                            = "subnet-adbcfd21"
      + tags                                 = {
          + "Name" = "dev-web-server"
        }
      + tags_all                             = {
          + "Name" = "dev-web-server"
        }
      + tenancy                              = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = [
          + "sg-3c57687c7fb5b2ca2",
        ]

      + capacity_reservation_specification (known after apply)

      + cpu_options (known after apply)

      + ebs_block_device (known after apply)

      + enclave_options (known after apply)

      + ephemeral_block_device (known after apply)

      + instance_market_options (known after apply)

      + maintenance_options (known after apply)

      + metadata_options (known after apply)

      + network_interface (known after apply)

      + primary_network_interface (known after apply)

      + private_dns_name_options (known after apply)

      + root_block_device (known after apply)

      + secondary_network_interface (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
aws_instance.web_server: Creating...
aws_instance.web_server: Still creating... [00m10s elapsed]
aws_instance.web_server: Still creating... [00m20s elapsed]
aws_instance.web_server: Still creating... [00m30s elapsed]
aws_instance.web_server: Creation complete after 39s [id=i-8e7ae0cc83a831907]

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

Create Outputs

output "vpc_id" {
  description = "The ID of the VPC"
  value       = aws_vpc.dev_vpc.id
}

output "public_subnet_id" {
  description = "The ID of the public subnet"
  value       = aws_subnet.public_subnet.id
}

output "instance_id" {
  description = "The ID of the EC2 instance"
  value       = aws_instance.web_server.id
}
terraform plan
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]
aws_security_group.web_sg: Refreshing state... [id=sg-3c57687c7fb5b2ca2]
aws_route_table.public_rt: Refreshing state... [id=rtb-c8bae0b5]
aws_instance.web_server: Refreshing state... [id=i-8e7ae0cc83a831907]
aws_route_table_association.public_assoc: Refreshing state... [id=rtbassoc-439ae214]

Changes to Outputs:
  + instance_id      = "i-8e7ae0cc83a831907"
  + public_subnet_id = "subnet-adbcfd21"
  + vpc_id           = "vpc-9de614ab"

You can apply this plan to save these new output values to the Terraform state, without changing
any real infrastructure.

────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
terraform apply -auto-approve
aws_vpc.dev_vpc: Refreshing state... [id=vpc-9de614ab]
aws_internet_gateway.igw: Refreshing state... [id=igw-e05f40c7]
aws_subnet.public_subnet: Refreshing state... [id=subnet-adbcfd21]
aws_security_group.web_sg: Refreshing state... [id=sg-3c57687c7fb5b2ca2]
aws_route_table.public_rt: Refreshing state... [id=rtb-c8bae0b5]
aws_route_table_association.public_assoc: Refreshing state... [id=rtbassoc-439ae214]
aws_instance.web_server: Refreshing state... [id=i-8e7ae0cc83a831907]

Changes to Outputs:
  + instance_id      = "i-8e7ae0cc83a831907"
  + public_subnet_id = "subnet-adbcfd21"
  + vpc_id           = "vpc-9de614ab"

You can apply this plan to save these new output values to the Terraform state, without changing
any real infrastructure.

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

Outputs:

instance_id = "i-8e7ae0cc83a831907"
public_subnet_id = "subnet-adbcfd21"
vpc_id = "vpc-9de614ab"
terraform output
instance_id = "i-8e7ae0cc83a831907"
public_subnet_id = "subnet-adbcfd21"
vpc_id = "vpc-9de614ab"