Skip to main content

AWS and AWSCC Terraform providers: Better together

Manage your cloud infrastructure with the AWS and AWSCC Terraform providers and view strategies on how to move state between providers.

Terraform has two AWS providers: AWS and AWSCC — one handwritten, the other automatically generated. But why the difference? In this post, we’ll dive into:

  • The reasoning behind these approaches
  • How each provider interacts with AWS APIs
  • How they complement rather than compete with each other

We’ll also share best practices for using them together and migrating resources from one provider to the other, without recreating them.

»AWS provider

The Terraform AWS provider was the first official Terraform provider for AWS integrations and is built on the AWS SDK. The provider celebrated its 10 year anniversary in 2024, and has surpassed three billion downloads. It includes resources for various AWS services, each making API calls to its respective service. For example, when Terraform creates an Amazon S3 bucket, the AWS provider calls the S3 CreateBucket API.

AWS provider flow

It’s good to note that all resources in the AWS provider, such as the aws_s3_bucket resource, are handwritten in Go. While this ensures rigorous test coverage, it also makes supporting all AWS resources — and especially day-one support for new services — a challenge due to the prioritization and manual effort involved.

»AWS Cloud Control API

AWS offers the Cloud Control API, a service that enables programmatic access to AWS features and services, often on the day they launch. The Cloud Control API remains up-to-date with the latest AWS resources, enabling developers to integrate their own solutions with Cloud Control API just once and then automatically access the new AWS services and features, without assuming additional integration work. In addition, it provides developers with a standardized set of CRUDL APIs for managing services in an intuitive and descriptive way.

»AWSCC provider

This consistency in the Cloud Control API enabled AWS and HashiCorp to develop the AWSCC provider which is generally available as of May 2024. Its uniform interface allows new resources to be automatically generated based on codified rules as soon as new resource schemas are released. Similar to the AWS provider, the AWSCC provider also interacts with AWS APIs. However, the difference is that the AWSCC only interacts with the AWS Cloud Control API when it creates resources.

AWSCC provider flow

Each resource interacts with the same Cloud Control API, supporting all or part of the five CRUDL operations: Create, Read, Update, Delete, and List. This also brings the added benefit of reducing the number of interface endpoints required to deploy infrastructure, especially in an air-gapped environment.The Cloud Control API provides an integrated interface endpoint.

In addition, since the AWSCC is automatically generated when new compatible services are supported by the Cloud Control API, it often provides launch-day support for new AWS services or features. While we aim to add similar support in the AWS provider, the AWSCC provider is more likely to support them first due to its generation process.

»Better together: Using both the AWS and AWSCC provider

The AWSCC provider is a great complementary provider to add to your existing Terraform configurations using the standard AWS provider. The AWS provider offers the best user experience and performance for over 1,300 resource types across nearly 200 services. The AWSCC provider builds on this by offering access to the latest AWS services generated from the Cloud Control API published by AWS. Using the AWSCC and AWS providers together equips developers with a large catalog of resources across established and new AWS services.

»Using the AWSCC provider

To deploy a simple VPC using the AWSCC provider, you can use the configuration example below. The developer experience is very similar to using the standard AWS provider — the main difference is that resource and data source names follow a slightly different naming convention.

provider "awscc" {
  region = "us-east-1"
}
 
resource "awscc_ec2_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}

»Using the AWSCC provider with the AWS provider

In the example below, the AWS provider is used to create a simple VPC, while the AWSCC provider creates a subnet within that VPC.

provider "aws" {
  region = "us-east-1"
}
 
provider "awscc" {
  region = "us-east-1"
}
 
resource "awscc_ec2_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "aws_subnet" "this" {
  vpc_id      = awscc_ec2_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

»Migrating a resource from AWSCC to the AWS provider

Use the following code as a starting point. This creates a VPC using the AWSCC provider and a subnet using the AWS provider. Run terraform init and terraform apply --auto-approve

provider "aws" {
  region = "us-east-1"
}
 
provider "awscc" {
  region = "us-east-1"
}
 
resource "awscc_ec2_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "aws_subnet" "this" {
  vpc_id     = awscc_ec2_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

To migrate the VPC from using the AWSCC provider to the AWS provider, replace the code above with the code below, then replace vpc-exampleID with the VPC ID from the previous step. Then run terraform apply --auto-approve. Note that in a live environment, you should test all the steps in a dev environment and review Terraform plans with terraform plan before approving any Terraform run.

provider "aws" {
  region = "us-east-1"
}
 
provider "awscc" {
  region = "us-east-1"
}
 
import {
  to = aws_vpc.this
  id = "vpc-exampleID"
}
 
removed {
  from = awscc_ec2_vpc.this
 
  lifecycle {
    destroy = false
  }
}
 
resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "aws_subnet" "this" {
  vpc_id     = aws_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

Here are the changes that were made in the code above:

1. Refactor awscc_ec2_vpc to aws_vpc and point the vpc_id argument of aws_subnet to aws_vpc. There are no further changes to the arguments for the VPC since both the AWSCC and AWS resource for VPC use the same cidr_block argument.

resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "aws_subnet" "this" {
  vpc_id     = aws_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

2. Add an import block to import aws_vpc.

import {
  to = aws_vpc.this
  id = "vpc-exampleID"
}

3. Add a removed block to remove awscc_ec2_vpc from state.

removed {
  from = awscc_ec2_vpc.this
 
  lifecycle {
    destroy = false
  }
}

»Migrating a resource from AWS to the AWSCC provider

Use the following code as a starting point. This creates a VPC using the AWSCC provider and a subnet using the AWS provider. Run terraform init and terraform apply --auto-approve

provider "aws" {
  region = "us-east-1"
}
 
provider "awscc" {
  region = "us-east-1"
}
 
resource "awscc_ec2_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "aws_subnet" "this" {
  vpc_id     = awscc_ec2_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

To migrate the subnet from using the AWS provider to the AWSCC provider, replace the code above with the code below, and replace the subnet-exampleID with the ID of the subnet created in the previous step. Then run terraform apply --auto-approve. Note that in a live environment, you should test all the steps in a dev environment and review Terraform plans with terraform plan before approving any Terraform run.

provider "aws" {
  region = "us-east-1"
}
 
provider "awscc" {
  region = "us-east-1"
}
 
import {
  to = awscc_ec2_subnet.this
  id = "subnet-exampleID"
}
 
removed {
  from = aws_subnet.this
 
  lifecycle {
    destroy = false
  }
}
 
resource "awscc_ec2_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "awscc_ec2_subnet" "this" {
  vpc_id     = awscc_ec2_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

Here are the changes that were made in the code above:

1. Refactor aws_subnet to awscc_ec2_subnet. There are no further changes to the arguments for the VPC since both the AWSCC and AWS resource for the subnet use the same vpc_id and cidr_block argument.

resource "awscc_ec2_vpc" "this" {
  cidr_block = "10.0.0.0/16"
}
 
resource "awscc_ec2_subnet" "this" {
  vpc_id     = awscc_ec2_vpc.this.id
  cidr_block = "10.0.1.0/24"
}

2. Add an import block to import awscc_ec2_subnet.

import {
  to = awscc_ec2_subnet.this
  id = "subnet-exampleID"
}

3. Add a removed block to remove aws_subnet from state.

removed {
  from = aws_subnet.this
 
  lifecycle {
    destroy = false
  }
}

»Learn more

For more resources on how to use the AWS and AWSCC providers providers together, refer to this AWS Workshop for managing cloud resources with Terraform. If you would like to see more examples of migrating state between providers and a few gotchas encountered, refer to this HashiTalk on migrating state between providers.

Sign up for the latest HashiCorp news

By submitting this form, you acknowledge and agree that HashiCorp will process your personal information in accordance with the Privacy Policy.