Terraform Basics for AWS: Build Your First Resource

DevOps 9 min readPublished 3 September 2026

Quick answer

Learn how Terraform providers, state and variables work with AWS. Follow a practical lab to create, inspect, change and safely destroy an S3 bucket.

Terraform is an Infrastructure as Code tool used to define cloud resources in configuration files. Instead of creating resources manually in the AWS Management Console, you describe the required infrastructure and let Terraform build and track it.

This guide covers the essential terraform basics aws learners need: providers, state, variables, plans and the resource lifecycle. The practical lab creates an Amazon S3 bucket, updates its tags and removes it safely.

What Is Terraform and How Does It Work with AWS?

Terraform reads declarative configuration files and uses the AWS provider to call AWS APIs. You describe the required end state, while Terraform determines which create, update or delete operations are necessary.

Terraform follows a predictable workflow:

  1. Write configuration in .tf files.
  2. Run terraform init to install providers.
  3. Run terraform plan to preview changes.
  4. Run terraform apply to make the changes.
  5. Store the resulting resource mapping in Terraform state.

A diagram in words looks like this:

Engineer
   |
   | writes .tf configuration
   v
Terraform CLI ---- reads ----> Terraform state
   |
   | uses AWS provider
   v
AWS APIs ---- create/update/delete ----> AWS resources

Terraform configuration is declarative. For example, you declare that an S3 bucket should exist in a particular region. You do not write separate scripts for creating the bucket, checking whether it exists and updating it.

Terraform is commonly used with automated build and deployment workflows. Learners who want guided practice across Terraform, AWS, Linux and CI/CD can review the AWS DevOps course.

What Are Terraform Providers?

A provider is a plugin that allows Terraform to communicate with an external platform or service. The HashiCorp AWS provider translates Terraform resource definitions into AWS API requests.

Providers support two main types of objects:

Object typePurposeAWS example
ResourceCreates or manages infrastructureaws_s3_bucket
Data sourceReads existing informationdata.aws_caller_identity

Create a new working directory:

mkdir terraform-aws-first-resource
cd terraform-aws-first-resource

Create versions.tf:

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.0"
    }
  }
}

The source identifies the provider in the Terraform Registry. The version constraint allows compatible releases in the selected major version while preventing an automatic upgrade to a future major version.

Create provider.tf:

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      ManagedBy = "Terraform"
      Lab       = "terraform-basics"
    }
  }
}

Do not place an AWS access key or secret access key in this file. Terraform can use credentials from the AWS CLI profile, environment variables, an EC2 instance role or other supported AWS credential sources.

For a local lab, configure the AWS CLI first:

aws configure
aws sts get-caller-identity

The second command confirms which AWS account and identity will be used. AWS permissions should follow least privilege; see AWS IAM users, roles and policies before running Terraform in a shared or production account.

What Is Terraform State?

Terraform state records the relationship between resource addresses in your configuration and real infrastructure. Without state, Terraform would not reliably know that an existing AWS bucket belongs to a particular resource block.

By default, local state is stored in:

terraform.tfstate

After an apply, the relationship can be pictured as:

Configuration address                  State mapping                 AWS object
aws_s3_bucket.lab_bucket  <---->  terraform.tfstate  <---->  my-unique-bucket

State is not the infrastructure itself. Deleting a state file does not delete the AWS resource, but it removes Terraform's record of that resource and can lead to duplicate-resource errors or unmanaged infrastructure.

Useful state inspection commands include:

terraform state list
terraform state show aws_s3_bucket.lab_bucket
terraform show

Treat state as sensitive because it can contain resource attributes and, depending on the resources used, confidential values. Do not commit terraform.tfstate, backup state files or crash logs to Git.

A suitable .gitignore is:

.terraform/
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfvars
!example.tfvars

Commit .terraform.lock.hcl to version control. It records provider version selections and checksums, helping team members and pipelines install consistent provider builds.

Local state is acceptable for an individual learning lab. Teams normally use a remote backend, access controls, encryption, versioning and state locking so that two engineers do not modify the same state concurrently.

How Do Terraform Variables Work?

Variables make a configuration reusable without editing resource blocks for every environment. A variable can define its type, description, default value, validation rules and whether its value is sensitive.

Create variables.tf:

variable "aws_region" {
  description = "AWS region for the lab resources"
  type        = string
  default     = "ap-south-1"
}

variable "bucket_name" {
  description = "Globally unique S3 bucket name"
  type        = string

  validation {
    condition = (
      length(var.bucket_name) >= 3 &&
      length(var.bucket_name) <= 63 &&
      can(regex("^[a-z0-9][a-z0-9.-]*[a-z0-9]$", var.bucket_name))
    )
    error_message = "Use 3-63 lowercase letters, numbers, periods or hyphens, starting and ending with a letter or number."
  }
}

variable "environment" {
  description = "Environment tag applied to resources"
  type        = string
  default     = "lab"
}

Create terraform.tfvars and choose a bucket name that is globally unique across AWS:

aws_region  = "ap-south-1"
bucket_name = "replace-with-your-unique-terraform-lab-name"
environment = "lab"

Terraform can receive variable values from several sources. Common methods are:

MethodExampleSuitable use
Default valuedefault = "ap-south-1"Safe standard value
Variable fileterraform.tfvarsLocal environment settings
CLI option-var="environment=test"Temporary override
Environment variableTF_VAR_environment=testCI/CD pipeline

Do not store passwords, access keys or other secrets in committed .tfvars files. Marking a variable as sensitive hides it from normal CLI display, but sensitive values may still be stored in state.

How Do You Create Your First AWS Resource with Terraform?

Define an aws_s3_bucket resource, initialise the working directory and review the plan before applying it. This lab uses an empty bucket because it is straightforward to inspect and remove, but AWS charges and account policies must still be considered.

Create main.tf:

resource "aws_s3_bucket" "lab_bucket" {
  bucket        = var.bucket_name
  force_destroy = false

  tags = {
    Name        = var.bucket_name
    Environment = var.environment
    Purpose     = "Terraform basics lab"
  }
}

The first quoted value, aws_s3_bucket, is the resource type. The second, lab_bucket, is Terraform's local name. Together they form the resource address:

aws_s3_bucket.lab_bucket

Create outputs.tf:

output "bucket_arn" {
  description = "ARN of the S3 bucket created by Terraform"
  value       = aws_s3_bucket.lab_bucket.arn
}

output "bucket_region" {
  description = "AWS region containing the bucket"
  value       = aws_s3_bucket.lab_bucket.region
}

Outputs expose selected information after an apply. They can also pass values between Terraform modules or into automation workflows.

Step 1: Format and validate the configuration

Run:

terraform fmt
terraform init
terraform validate

terraform fmt applies standard HCL formatting. terraform init downloads the AWS provider and creates .terraform.lock.hcl, while terraform validate checks the configuration structure and references.

A successful initialisation includes messages similar to:

Initializing provider plugins...
- Installing hashicorp/aws v6.x.x...
Terraform has been successfully initialized!

The exact provider patch version depends on the constraint and lock file.

Step 2: Preview the changes

Create and save an execution plan:

terraform plan -out=tfplan

The plan should contain a line similar to:

# aws_s3_bucket.lab_bucket will be created
+ resource "aws_s3_bucket" "lab_bucket" {
    + arn    = (known after apply)
    + bucket = "your-unique-bucket-name"
  }

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

A plus sign means create, a tilde means update in place, and a minus sign means destroy. The phrase known after apply means AWS will return that value only after the resource is created.

Always inspect the plan for the expected AWS account, region, resource names and destructive actions.

Step 3: Apply the saved plan

Run:

terraform apply tfplan

Because the saved plan was already reviewed, Terraform applies those exact planned actions without asking for another approval. Verify the result with both Terraform and AWS CLI commands:

terraform state list
terraform output
aws s3api head-bucket --bucket YOUR_UNIQUE_BUCKET_NAME

The state list should show:

aws_s3_bucket.lab_bucket

Those building broader AWS design skills can continue from this lab into identity, storage, networking and reliability topics covered in an AWS Solutions Architect course.

How Does Terraform Handle Configuration Changes?

Terraform compares the current configuration, saved state and provider-reported infrastructure to calculate a new plan. Where the AWS API supports modification, Terraform can update a resource without replacing it.

Change the environment value in terraform.tfvars:

environment = "development"

Then run:

terraform plan

The plan should show an in-place tag update, indicated by ~, rather than a replacement. Apply it with:

terraform apply

For an interactive apply, Terraform displays a fresh plan and requires yes before continuing.

Not every property can be changed in place. Some changes force resource replacement, which appears as -/+ in the plan. Review replacement actions carefully because they can cause downtime or data loss.

Terraform also checks for drift during planning and refresh operations. Drift occurs when someone changes a managed resource outside Terraform, such as through the AWS console. The next plan may propose restoring the configured value or adapting the resource to the new configuration.

How Do You Troubleshoot Common Terraform AWS Errors?

Most beginner errors come from credentials, permissions, duplicate names, invalid configuration or state mismatches. Read the full error message and identify whether it came from Terraform validation, the provider or an AWS API.

Error: No valid credential sources found

Confirm that the AWS CLI can authenticate:

aws sts get-caller-identity
aws configure list

If using a named profile, select it before running Terraform:

export AWS_PROFILE=lab-profile
terraform plan

On PowerShell, use:

$env:AWS_PROFILE = "lab-profile"
terraform plan

Error: AccessDenied

The authenticated identity lacks permission for the requested API operation. Check the operation named in the error and review the user's or role's IAM policies, permission boundaries and applicable AWS Organizations service control policies.

Do not solve every lab permission error by attaching unrestricted administrator access. Grant only the actions and resources needed by the lab where practical.

Error: BucketAlreadyExists

S3 general purpose bucket names are globally unique across AWS accounts and Regions within the AWS partition. Change bucket_name to a genuinely unique lowercase name and run terraform plan again.

Error: Inconsistent dependency lock file

This can occur when a saved plan no longer matches the selected provider dependencies. Reinitialise and create a new plan:

terraform init
terraform plan -out=tfplan
terraform apply tfplan

Do not apply an old saved plan after changing configuration or provider selections.

Terraform says the resource already exists

If an AWS resource exists but is not in the current state, Terraform may try to create it again. Confirm the situation before importing it:

terraform state list
aws s3api head-bucket --bucket YOUR_UNIQUE_BUCKET_NAME

An existing bucket that you own can be imported with:

terraform import aws_s3_bucket.lab_bucket YOUR_UNIQUE_BUCKET_NAME
terraform plan

Import adds the resource mapping to state; it does not automatically write the complete desired configuration. The resource block must still match the settings you intend to manage.

How Do You Safely Destroy the Lab Resource?

Use terraform destroy to review and delete resources managed by the current configuration and state. Confirm that you are in the correct directory, using the correct AWS account and targeting only disposable lab infrastructure.

Preview destruction first:

terraform plan -destroy

Then remove the bucket:

terraform destroy

Terraform will ask for confirmation. Enter yes only after checking the plan.

The example uses force_destroy = false. If objects have been uploaded to the bucket, AWS will refuse to delete the non-empty bucket. Remove the objects intentionally before running destroy rather than changing this setting without reviewing the data.

After destruction, verify:

terraform state list

No resource address should remain for the deleted bucket.

Summary

Terraform uses providers to communicate with AWS, variables to make configurations reusable and state to map code to real resources. The safe working cycle is format, initialise, validate, plan, apply, inspect and destroy.

A good next step is to place the configuration in Git without state or secrets, add an explicit S3 public access block, and practise remote state in a controlled account. You can then progress to modules, IAM roles, VPC resources and CI/CD-based Terraform execution.

For instructor-led labs on Terraform, AWS services, Linux and deployment pipelines, enquire about schedules and batch details for the AWS DevOps course.

*Reviewed by Network Rhinos AWS and DevOps trainers.*

Related reading: Amazon EC2 Explained: Instances, AMIs, Storage and Security

Frequently asked questions

Do I need an AWS account to practise Terraform?

Yes, you need an AWS account to create real AWS resources. Use a dedicated lab account where possible, review the Terraform plan and delete resources after practice to control costs.

Does Terraform replace the AWS Management Console?

Terraform can manage many AWS resources without manual console creation, but the console remains useful for inspection and troubleshooting. Teams often use Terraform as the controlled source of infrastructure changes rather than making routine manual changes.

What is the difference between a Terraform provider and a resource?

A provider is the plugin that communicates with a platform such as AWS. A resource is a specific infrastructure object managed through that provider, such as an S3 bucket, VPC or EC2 instance.

Should terraform.tfstate be committed to Git?

No, local state and its backup files should not be committed to Git because they may contain sensitive resource information. Team environments should use a protected remote backend with appropriate access controls, encryption, versioning and locking.

Why must an S3 bucket name be unique?

Amazon S3 general purpose bucket names are globally unique across accounts and Regions within an AWS partition. If another account already owns the requested name, Terraform receives a BucketAlreadyExists error.

What is the difference between terraform plan and terraform apply?

Terraform plan previews the actions required to match the configuration without intentionally making those changes. Terraform apply executes an approved plan and then updates state to record the resulting infrastructure.

Related articles

Train with Network Rhinos

Hands-on CCNA, CCNP, AWS, Azure, DevOps and cybersecurity training in Chennai & Bangalore, with placement support. Talk to our team or attend a free demo class.