Terraform Remote State

In: IaC, AWS, NetDevOps

Terraform state

Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real-world resources to your configuration, keep track of metadata, and improve performance for large infrastructures.

This state is stored by default in a local file named "terraform.tfstate" (shown below), but it can also be stored remotely, which works better in a team environment.

In simple words, Terraform should know what resources are supposed to manage. Terraform keeps a record of our Infrastructure in a state file.

Suresh-MacBook:my-account sureshvinasiththamby$ ls -l
total 40
-rw-r--r--@ 1 sureshvinasiththamby  staff   135 28 Apr 13:58 backend-s3.tf
-rw-r--r--  1 sureshvinasiththamby  staff   157 28 Apr 13:50 terraform.tfstate
-rw-r--r--  1 sureshvinasiththamby  staff  2526 28 Apr 13:50 terraform.tfstate.backup
-rw-r--r--@ 1 sureshvinasiththamby  staff  1417 28 Apr 13:49 vpc-ec2.tf

In this article. I will show you how to save the state in the AWS S3 bucket.

Terraform Remote backend

Remote backends allow Terraform to use a shared storage space for state data, so any member of your team can use Terraform to manage the same infrastructure.

Step 1 - Create an S3 bucket

Step -2 Configure Terraform backend definition.

I created a separate backend.tf file in the same directory. With this configuration, we are telling Terraform to save the state file in terraform-state-packet bucket. Key is the name that you assign to an object which uniquely identifies the object in the bucket. (The file path)

terraform {
  backend "s3" {
    bucket  = "terraform-state-packet"
    key     = "packetswitch/demo"
    region  = "eu-west-2"


To demonstrate this, I am going to create a VPC and a subnet in eu-west-2 region. Once we apply this, Terraform should update the remote state file with the changes.

provider "aws" {
  region  = "eu-west-2"

resource "aws_vpc" "test-vpc" {
  cidr_block       = ""
  tags = {
    Name = "terraform-test"


resource "aws_subnet" "test-public-subnet" {
    vpc_id = aws_vpc.test-vpc.id
    cidr_block = ""
    map_public_ip_on_launch = "true"
    availability_zone = "eu-west-2a"
    tags = {
        Name = "public-subnet"

Terraform should now save this new state in S3.

Suresh-MacBook:my-account sureshvinasiththamby$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Suresh-MacBook:my-account sureshvinasiththamby$ terraform apply

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_subnet.test-public-subnet will be created
  + resource "aws_subnet" "test-public-subnet" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "eu-west-2a"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = ""
      + id                              = (known after apply)
      + ipv6_cidr_block                 = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = true
      + owner_id                        = (known after apply)
      + tags                            = {
          + "Name" = "public-subnet"
      + vpc_id                          = (known after apply)

  # aws_vpc.test-vpc will be created
  + resource "aws_vpc" "test-vpc" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = ""
      + 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_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "Name" = "terraform-test"

Plan: 2 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: yes

aws_vpc.test-vpc: Creating...
aws_vpc.test-vpc: Creation complete after 2s [id=vpc-032195d344c8b0c2f]
aws_subnet.test-public-subnet: Creating...
aws_subnet.test-public-subnet: Creation complete after 1s [id=subnet-09e471b369f939c0e]

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

If you head over to S3, you should be able to see the state file in the bucket.

The file contains the state of the VPC and subnet we just created.

  "version": 4,
  "terraform_version": "0.12.24",
  "serial": 2,
  "lineage": "b1164ae3-2977-8cce-9a0f-ad8474c46d79",
  "outputs": {},
  "resources": [
      "mode": "managed",
      "type": "aws_subnet",
      "name": "test-public-subnet",
      "provider": "provider.aws",
      "instances": [
          "schema_version": 1,
          "attributes": {
            "arn": "arn:aws:ec2:eu-west-2:488663852689:subnet/subnet-09e471b369f939c0e",
            "assign_ipv6_address_on_creation": false,
            "availability_zone": "eu-west-2a",
            "availability_zone_id": "euw2-az2",
            "cidr_block": "",
            "id": "subnet-09e471b369f939c0e",
            "ipv6_cidr_block": "",
            "ipv6_cidr_block_association_id": "",
            "map_public_ip_on_launch": true,
            "owner_id": "**********",
            "tags": {
              "Name": "public-subnet"
            "timeouts": null,
            "vpc_id": "vpc-032195d344c8b0c2f"
          "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9",
          "dependencies": [
      "mode": "managed",
      "type": "aws_vpc",
      "name": "test-vpc",
      "provider": "provider.aws",
      "instances": [
          "schema_version": 1,
          "attributes": {
            "arn": "arn:aws:ec2:eu-west-2:488663852689:vpc/vpc-032195d344c8b0c2f",
            "assign_generated_ipv6_cidr_block": false,
            "cidr_block": "",
            "default_network_acl_id": "acl-026e7af044870168c",
            "default_route_table_id": "rtb-0fdab40d378fa9466",
            "default_security_group_id": "sg-015d16faa69f6d395",
            "dhcp_options_id": "dopt-19a7f871",
            "enable_classiclink": null,
            "enable_classiclink_dns_support": null,
            "enable_dns_hostnames": false,
            "enable_dns_support": true,
            "id": "vpc-032195d344c8b0c2f",
            "instance_tenancy": "default",
            "ipv6_association_id": "",
            "ipv6_cidr_block": "",
            "main_route_table_id": "rtb-0fdab40d378fa9466",
            "owner_id": "**********",
            "tags": {
              "Name": "terraform-test"
          "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ=="


State - Terraform by HashiCorp
Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
How to manage Terraform state
Update, November 17, 2016: We took this blog post series, expanded it, and turned it into a book called Terraform: Up & Running! Update, July 8, 2019: We’ve updated this blog post series for…
Written by
Suresh Vinasiththamby
I'm very excited to start blogging and share with you insights about my favourite Networking, Cloud and Automation topics. Simple guy with simple taste and lots of love for Networking and Automation.
More from Packetswitch
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Packetswitch.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.