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.