100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
HCL

Provisioning AWS Resources

A practical walkthrough of configuring the AWS provider and provisioning core compute, networking, and storage resources with Terraform.

Multi-Cloud & ProvisioningIntermediate10 min readJul 9, 2026
Analogies

Provisioning AWS Resources

AWS is the most widely used Terraform provider, and the hashicorp/aws provider exposes hundreds of resource types covering compute, networking, storage, IAM, and managed services. Provisioning AWS infrastructure with Terraform follows a consistent pattern regardless of the specific service: configure the provider block with a region (and typically rely on credentials from the environment or an IAM role rather than hardcoding them), then declare resource blocks whose arguments map closely to the fields you'd otherwise set in the AWS console or via the AWS CLI.

🏏

Cricket analogy: Just as the IPL is the most-watched league with dozens of standardized match formats covering batting, bowling, and fielding roles, AWS is the most widely used provider with hundreds of resource types spanning every infrastructure need.

Configuring the provider

The aws provider block accepts a region argument and, optionally, a profile or explicit access key/secret pair, though the strongly recommended practice is to leave credentials out of the configuration entirely and let the AWS SDK's standard credential chain resolve them from environment variables, an EC2/ECS instance role, or an assumed role via STS. Pinning the provider version with a required_providers block prevents an unexpected major-version upgrade of the AWS provider from silently changing resource schemas between applies.

🏏

Cricket analogy: A visiting team doesn't carry cash to bribe local officials; they rely on the board's official accreditation chain to grant ground access, and they lock the tour itinerary's format version so a late rule change doesn't upend it.

hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
  # Credentials resolved from environment variables or an assumed IAM role,
  # not hardcoded here.
}

A typical compute and networking stack

A common baseline stack provisions a VPC, at least one subnet, a security group, and an EC2 instance, wired together through implicit dependencies. Real-world configurations almost always also attach an IAM instance profile so the instance can call other AWS services without embedded credentials, and use data sources like aws_ami to look up the latest approved AMI rather than hardcoding an AMI ID that will eventually go stale.

🏏

Cricket analogy: A domestic team fields a full XI with an opener, a wicketkeeper, and specialist bowlers wired together by role, and a well-run franchise also grants players a dedicated support staff badge rather than sharing one generic pass among everyone.

hcl
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}

resource "aws_security_group" "web" {
  name   = "web-sg"
  vpc_id = aws_vpc.main.id

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

resource "aws_instance" "web" {
  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = "t3.micro"
  subnet_id              = aws_subnet.main.id
  vpc_security_group_ids = [aws_security_group.web.id]

  tags = {
    Name        = "web-server"
    Environment = "production"
  }
}

Terraform's AWS provider is essentially a thin, declarative layer over the AWS API: nearly every resource argument maps directly to a field in the corresponding AWS API request, which is why the AWS API reference is often the fastest way to understand what an unfamiliar resource argument actually controls.

Storage resources such as aws_s3_bucket and aws_db_instance require extra care: an accidental terraform destroy or a misconfigured lifecycle can permanently delete data. Use prevent_destroy in a lifecycle block and enable versioning/deletion protection on stateful resources in production.

Managed services and IAM

Beyond raw compute, most production AWS stacks provision managed services — RDS databases, S3 buckets, Lambda functions, ECS/EKS clusters — alongside the IAM roles and policies that let those services talk to each other securely. Because IAM resources are cheap to create and central to least-privilege security, well-structured AWS Terraform configurations typically define a dedicated IAM role and a narrowly scoped policy document per workload rather than reusing broad, shared roles.

🏏

Cricket analogy: Beyond the playing XI, a franchise also runs an academy, a physio unit, and a media team, each given narrowly scoped access to specific facilities rather than every staff member sharing one master keycard to the whole complex.

  • Pin the AWS provider version with required_providers to avoid unexpected schema changes on upgrade.
  • Avoid hardcoding AWS credentials in configuration; rely on the standard credential chain (env vars, instance roles, assumed roles).
  • Use data sources like aws_ami to avoid hardcoding values (like AMI IDs) that go stale over time.
  • Wire compute, networking, and security group resources together through implicit dependencies on their attributes.
  • Attach narrowly scoped IAM roles/policies per workload instead of reusing broad shared roles.
  • Protect stateful resources (RDS, S3) with lifecycle prevent_destroy and versioning/deletion protection.

Practice what you learned

Was this page helpful?

Topics covered

#HCL#TerraformInfrastructureAsCodeStudyNotes#DevOps#ProvisioningAWSResources#Provisioning#AWS#Resources#Configuring#CloudComputing#StudyNotes#SkillVeris