# Using Multiple Terraform's Providers With AWS

Originally posted on 29 May 2020.

### What Terraform Providers are?

Terraform providers are the Terraform internal component responsible for API understanding and API communication.  
Providers also understand authentication and authorization for the external API.

### AWS Provider

One of the most used Terraform providers is the [AWS Terraform Provider](https://www.terraform.io/docs/providers/aws/index.html). This provider handles all the communication with AWS API allowing you to provision resources on AWS.

AWS Provider Example Usage (from Terraform docs)

```plaintext
# Configure the AWS Provider
provider "aws" {
  region  = "us-east-1"
}

terraform {
  required_version = "= 0.15.4"

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

# Create a VPC
resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"
}
```

Without special needs, most users stop at these simple provider definitions, but AWS provider (like other providers) has a lot of possible customizations. Some examples:

```plaintext
# Configure the AWS Provider
provider "aws" {
  region  = "us-east-1"

  profile = "my-custom-aws-profile"
  shared_credentials_file = "~/.aws/credentials"

  allowed_account_ids = ["123456789012"]
  forbidden_account_ids = ["98765432198"]
}

terraform {
  required_version = "= 0.15.4"

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

Parameters like `allowed_account_ids` and `forbidden_account_ids` help you avoid applying modification on the wrong account by simply using the wrong profile or environment variables.

### Multiple Providers

Now the article's main topic: using multiple AWS providers.  
A single Terraform AWS provider is limited to an account and a specific region.  
What if we want to deploy resources within the same stack but in two regions. This need is not as remote as you can imagine, it's actually a pretty common use case.  
When you use a CloudFront Distribution with a custom SSL certificate you have to create an ACM certificate in **N.Virginia**.  
So here we have a small example of a CloudFront distribution in **Ireland** and the needed ACM Certificate in **N.Virginia**, all within the same stack and even within the same file.

```plaintext
provider "aws" {
  region = "eu-west-1"
}

provider "aws" {
  alias  = "uswest"
  region = "us-east-1"
}

terraform {
  required_version = "= 0.15.4"

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

To use our multiple providers:

```plaintext
resource "aws_cloudfront_distribution" "distribution" {
    viewer_certificate_arguments {
        acm_certificate_arn = aws_acm_certificate.cert.arn
        ....
    }
    ....
}

resource "aws_acm_certificate" "cert" {
    provider = aws.uswest
    ....
}
```

### Example (S3 Cross-Region Replication)

```plaintext
resource "aws_s3_bucket" "uswest_bucket" {
  provider = aws.uswest
  bucket   = "alessandromarinoac-uswest-bucket"
  acl      = "private"
  versioning {
    enabled = true
  }
}

resource "aws_s3_bucket" "euwest_bucket" {
  bucket = "alessandromarinoac-euwest-bucket"
  acl    = "private"
  versioning {
    enabled = true
  }

  replication_configuration {
    role = aws_iam_role.s3_fullaccess_role.arn
    rules {
      id     = "testReplication"
      status = "Enabled"
      destination {
        bucket        = aws_s3_bucket.uswest_bucket.arn
        storage_class = "STANDARD"
      }
    }
  }
}

resource "aws_iam_role" "s3_fullaccess_role" {
  name               = "s3-fullaccess-role"
  assume_role_policy = data.aws_iam_policy_document.s3_fullaccess_role_assume_policy.json
}

resource "aws_iam_role_policy" "s3_fullaccess_policy" {
  name   = "s3_fullaccess_policy"
  role   = aws_iam_role.s3_fullaccess_role.id
  policy = data.aws_iam_policy_document.s3_fullaccess_role_policy.json
}

data "aws_iam_policy_document" "s3_fullaccess_role_assume_policy" {
  statement {
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["s3.amazonaws.com"]
    }
    actions = [
      "sts:AssumeRole"
    ]
  }
}

data "aws_iam_policy_document" "s3_fullaccess_role_policy" {
  statement {
    effect = "Allow"
    actions = [
      "s3:GetReplicationConfiguration",
      "s3:ListBucket"
    ]
    resources = [
      "arn:aws:s3:::alessandromarinoac-euwest-bucket",
    ]
  }

  statement {
    effect = "Allow"
    actions = [
      "s3:GetObjectVersion",
      "s3:GetObjectVersionAcl",
      "s3:GetObjectVersionTagging"
    ]
    resources = [
      "arn:aws:s3:::alessandromarinoac-euwest-bucket/*"
    ]
  }

  statement {
    effect = "Allow"
    actions = [
      "s3:ReplicateObject",
      "s3:ReplicateDelete",
      "s3:ReplicateTags"
    ]
    resources = [
      "${aws_s3_bucket.uswest_bucket.arn}/*"
    ]
  }
}
```

The example will create two S3 buckets in two different AWS regions:

![Terraform Multi Provider Buckets](https://s3-eu-west-1.amazonaws.com/alessandromarinoac.com/MultiProvider.PNG align="left")

With replication enabled:

![Terraform Multi Provider Replication](https://s3-eu-west-1.amazonaws.com/alessandromarinoac.com/MultiProvider1.PNG align="left")
