AWS Provider Setup
The AWS provider is Terraform’s gateway to Amazon Web Services, but configuring it properly for production use involves more than just setting a region. Authentication strategies, provider aliases for multi-region deployments, and proper credential management are essential for building reliable, secure infrastructure automation.
Getting the provider configuration right from the start prevents authentication headaches, security issues, and deployment failures down the road. The patterns in this part work whether you’re managing a single AWS account or a complex multi-account organization.
Authentication Strategies
AWS credentials can be provided to Terraform in several ways, each with different security and operational implications:
Environment variables (recommended for local development):
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_DEFAULT_REGION="us-west-2"
terraform plan
AWS CLI profiles for multiple account management:
# Configure profiles
aws configure --profile dev
aws configure --profile prod
# Use with Terraform
export AWS_PROFILE=dev
terraform plan
IAM roles (recommended for production):
provider "aws" {
region = "us-west-2"
assume_role {
role_arn = "arn:aws:iam::123456789012:role/TerraformRole"
session_name = "terraform-session"
}
}
Instance profiles for EC2-based CI/CD:
provider "aws" {
region = "us-west-2"
# Automatically uses instance profile when running on EC2
}
Multi-Region Provider Configuration
Real AWS architectures often span multiple regions for disaster recovery, compliance, or performance reasons:
# Primary region provider
provider "aws" {
region = "us-west-2"
alias = "primary"
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = var.project_name
}
}
}
# Secondary region for DR
provider "aws" {
region = "us-east-1"
alias = "dr"
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = var.project_name
}
}
}
# Use providers in resources
resource "aws_s3_bucket" "primary" {
provider = aws.primary
bucket = "my-app-primary-${var.environment}"
}
resource "aws_s3_bucket" "dr" {
provider = aws.dr
bucket = "my-app-dr-${var.environment}"
}
Cross-Account Provider Setup
Multi-account AWS architectures require careful provider configuration:
# Shared services account
provider "aws" {
region = "us-west-2"
alias = "shared"
assume_role {
role_arn = "arn:aws:iam::111111111111:role/TerraformCrossAccountRole"
}
}
# Production account
provider "aws" {
region = "us-west-2"
alias = "prod"
assume_role {
role_arn = "arn:aws:iam::222222222222:role/TerraformCrossAccountRole"
}
}
# Create resources in different accounts
resource "aws_route53_zone" "shared" {
provider = aws.shared
name = "example.com"
}
resource "aws_route53_record" "prod" {
provider = aws.prod
zone_id = aws_route53_zone.shared.zone_id
name = "api.example.com"
type = "A"
ttl = 300
records = [aws_instance.api.public_ip]
}
Provider Version Management
Pin provider versions to ensure consistent deployments:
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.20"
}
}
}
# Provider configuration
provider "aws" {
region = var.aws_region
# Skip metadata API check for faster provider initialization
skip_metadata_api_check = true
# Skip region validation for custom regions
skip_region_validation = false
# Skip credentials validation for faster startup
skip_credentials_validation = false
}
Default Tags and Resource Naming
Consistent tagging and naming are crucial for AWS cost management and organization:
provider "aws" {
region = "us-west-2"
default_tags {
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "terraform"
Owner = var.team_name
CostCenter = var.cost_center
CreatedDate = formatdate("YYYY-MM-DD", timestamp())
}
}
}
# Local values for consistent naming
locals {
name_prefix = "${var.project_name}-${var.environment}"
common_tags = {
Application = var.application_name
Component = "infrastructure"
}
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = merge(local.common_tags, {
Name = "${local.name_prefix}-vpc"
Type = "networking"
})
}
AWS CLI Integration
Terraform works best when integrated with AWS CLI workflows:
# Validate AWS credentials
aws sts get-caller-identity
# Check current region
aws configure get region
# List available regions
aws ec2 describe-regions --query 'Regions[].RegionName' --output table
# Validate IAM permissions
aws iam simulate-principal-policy \
--policy-source-arn $(aws sts get-caller-identity --query Arn --output text) \
--action-names ec2:DescribeInstances \
--resource-arns "*"
Environment-Specific Configuration
Different environments often need different provider configurations:
# variables.tf
variable "environment" {
description = "Environment name"
type = string
}
variable "aws_region" {
description = "AWS region"
type = string
default = "us-west-2"
}
variable "assume_role_arn" {
description = "IAM role ARN to assume"
type = string
default = null
}
# main.tf
provider "aws" {
region = var.aws_region
dynamic "assume_role" {
for_each = var.assume_role_arn != null ? [1] : []
content {
role_arn = var.assume_role_arn
}
}
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
}
Security Best Practices
Secure provider configuration prevents credential leaks and unauthorized access:
Never hardcode credentials:
# DON'T DO THIS
provider "aws" {
access_key = "AKIAIOSFODNN7EXAMPLE" # Never hardcode!
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
# DO THIS INSTEAD
provider "aws" {
region = "us-west-2"
# Use environment variables, profiles, or IAM roles
}
Use least-privilege IAM policies:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:CreateTags",
"ec2:RunInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": ["us-west-2", "us-east-1"]
}
}
}
]
}
Enable CloudTrail logging:
resource "aws_cloudtrail" "terraform_audit" {
name = "terraform-audit-${var.environment}"
s3_bucket_name = aws_s3_bucket.audit_logs.bucket
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::S3::Object"
values = ["${aws_s3_bucket.terraform_state.arn}/*"]
}
}
}
Troubleshooting Common Issues
Authentication failures:
# Check current credentials
aws sts get-caller-identity
# Verify region configuration
echo $AWS_DEFAULT_REGION
# Test specific profile
aws sts get-caller-identity --profile myprofile
Provider initialization issues:
# Clear provider cache
rm -rf .terraform/
# Reinitialize with debug logging
TF_LOG=DEBUG terraform init
Cross-account access problems:
# Test role assumption
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/TerraformRole \
--role-session-name test-session
What’s Next
Proper AWS provider configuration is the foundation for everything else you’ll build with Terraform on AWS. With authentication, regions, and basic security patterns in place, you’re ready to tackle AWS networking—the backbone of well-architected cloud infrastructure.
In the next part, we’ll explore VPC design patterns, subnet strategies, and the networking building blocks that support scalable, secure AWS architectures.