A Guide to Effective Use of the Terraform AWS Cloud Control Provider

A Guide to Effective Use of the Terraform AWS Cloud Control Provider

Discover the recently GA'ed Terraform AWS Cloud Control Provider and how to effectively use it alongside the classic Terraform AWS Provider.

Introduction

The AWS Cloud Control (CC) Provider gained significant attention in May 2024 when it became generally available, three years after its initial launch. It promises to support new AWS features and services immediately due to its auto-generated nature, which is especially beneficial for the rapidly evolving generative AI services like Amazon Bedrock.

But should you immediately switch to the AWS CC Provider and abandon the classic AWS Provider? Not necessarily. While the CC Provider brings speed and coverage of new services, it’s not without limitations. In this blog post, we’ll break down the strengths and weaknesses of both providers, highlighting when it makes sense to leverage the AWS CC Provider and where the classic AWS Provider still shines. A practical example will demonstrate how both can be best used together.

Understanding the Strengths and Weaknesses of the AWS CC Provider

The main selling point of the AWS CC Provider is that it provides support for new AWS services sooner than the classic AWS Provider. The GA announcement showcases this speed by supporting the Amazon Q Business resources early on. In contrast, the enhancement request that was opened against the AWS Provider in January 2024 is still pending, even though a pull request (PR) has already been submitted by a contributor for some time. It makes sense to leverage the resources from the AWS CC Provider to not delay your IaC automation effort.

Even though the AWS CC Provider covers new AWS services quickly, there’s still a lot of room for improvement when it comes to older services. According to this GitHub issue, as of October 2023, the Cloud Control API supports only 859 resources, many of which are not supported in all AWS regions. According to Resource types that support Cloud Control API, as of July 2024 it supports 1034 resources, which is encouraging to see. However, there is still a list of suppressed resources that are not compatible with how the AWS CC Provider generates resources, bringing the actual supported number of resources to around 1,000. Compared to about 1,400 resources supported by the AWS Provider, that's only about 70% coverage and less if you consider resources that haven't been implemented in the AWS Provider.

While working with the AWS CC Provider, I noticed challenges with both documentation and quality assurance. The quality of descriptions for resources and attributes is somewhat inconsistent. For example, the documentation for the awscc_iam_group resource is quite well written, while the documentation for the awscc_qbusiness_application resource is practically non-existent. Overall, it pales in comparison to the AWS API documentation which I often refer to when contributing to the AWS Provider. I am not sure why the CloudFormation schemas (from which the AWS CC Provider resources and documentation are generated) are so far apart from the AWS API documentation, but I hope AWS can reconcile the two sources at some point.

As for the functional quality of the AWS CC Provider, my experience unfortunately hasn't been great. While adding examples to the Lightsail resources, I ran into two major functional issues and two documentation issues that are caused upstream in the Cloud Control API. This led me to believe that there is insufficient quality assurance with the Cloud Control API, and the generated nature of the AWS CC Provider does not help catch these issues. The situation will hopefully improve over time, but for the time being I would prefer the AWS Provider for mission-critical use. Nevertheless, I must give credit to the AWS CC Provider maintainers in diligently reporting and working with AWS to resolve upstream issues in a timely manner. The turnaround time is much quicker than if I were to open AWS support cases myself.

Also Knowing the Merits and Drawbacks of the Classic AWS Provider

The Terraform AWS Provider has been active for over ten years and has recently surpassed three billion downloads. The tremendous work that HashiCorp, AWS, and the community put into the provider over the years has led to high AWS service coverage. The provider boasts ample acceptance tests which result in a relatively high degree of quality. The user base also proactively reports less prevalent issues that are not caught by automated tests.

Since the AWS Provider code is not generated like the AWS CC Provider, the hand-crafted nature means that development is labor intensive and time consuming. Consequently, lower priority issues and features often take some time to be fixed. Even when a PR is submitted by a contributor, a maintainer from HashiCorp still needs to review, test, and merge it in between their other work such as those related to product roadmaps.

Meanwhile, the need for hands-on development also affords the flexibility to add custom logic to resources and data sources. As previously mentioned, the AWS API often does not fit perfectly into a CRUDL model due to actions that fall outside these operations. For instance, the Agents for Amazon Bedrock API has an AssociateAgentKnowledgeBase action that associates a knowledge base to an agent. Since it is not considered a resource in the AWS CC API, it is not mapped to a resource in the AWS CC Provider. However, an experienced developer for the AWS Provider is able to adapt this action into an "association", resulting in the aws_bedrockagent_agent_knowledge_base_association resource.

As another example, a Bedrock agent must be prepared using the PrepareAgent action after it is updated. Since this action cannot be easily adapted to a resource, the logical approach is to call this API action when an aws_bedrockagent_agent resource is created and updated, leading to the custom prepare_agent argument. Similar logic can be added to other Agents for Bedrock resources that indirectly modifies an agent. Having this type of custom resources and logic is only possible in the AWS Provider today.

Using Both Providers in a Complementary Manner

The good news is that Terraform is designed to work with multiple providers, so you can leverage both the AWS Provider and the AWS CC Provider for what they each excel at.

Let’s look at a use case of adding a guardrail to a Bedrock agent. Currently, the AWS Provider has the aws_bedrock_guardrail resource, but it does not yet have a resource to manage guardrail versions. While the aws_bedrock_agent resource has been around for some time, it does not yet have the configuration to associate a guardrail.

On the other hand, the AWS CC Provider has a awscc_bedrock_guardrail_version resource and the awscc_bedrock_agent resource supports the guardrail_configuration argument for associating a guardrail. Thus we can strategically use the AWS CC Provider for the new features while using the AWS Provider for all other resources.

Here is the Terraform configuration for a simple Bedrock agent that answers questions about world history, but is guarded against providing information on violent historical events like what happened to Julius Caesar:

data "aws_caller_identity" "this" {}
data "aws_partition" "this" {}
data "aws_region" "this" {}
locals {
  account_id = data.aws_caller_identity.this.account_id
  partition  = data.aws_partition.this.partition
  region     = data.aws_region.this.name
}

data "aws_bedrock_foundation_model" "this" {
  model_id = "anthropic.claude-3-haiku-20240307-v1:0"
}

resource "aws_bedrock_guardrail" "this" {
  name                      = "MyGuardrail"
  description               = "My guardrail"
  blocked_input_messaging   = "Sorry, I cannot answer this question."
  blocked_outputs_messaging = "Sorry, I cannot answer this question."
  content_policy_config {
    filters_config {
      input_strength  = "HIGH"
      output_strength = "HIGH"
      type            = "VIOLENCE"
    }
  }
}

resource "awscc_bedrock_guardrail_version" "this" {
  guardrail_identifier = aws_bedrock_guardrail.this.guardrail_id
  lifecycle {
    replace_triggered_by = [aws_bedrock_guardrail.this]
  }
}

resource "aws_iam_role" "bedrock_agent_this" {
  name = "AmazonBedrockExecutionRoleForAgents_MyAgent"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "bedrock.amazonaws.com"
        }
        Condition = {
          StringEquals = {
            "aws:SourceAccount" = local.account_id
          }
          ArnLike = {
            "aws:SourceArn" = "arn:${local.partition}:bedrock:${local.region}:${local.account_id}:agent/*"
          }
        }
      }
    ]
  })
}

resource "aws_iam_role_policy" "bedrock_agent_this" {
  name = "AmazonBedrockAgentBedrockFoundationModelPolicy_MyAgent"
  role = aws_iam_role.bedrock_agent_this.name
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "InvokeFoundationModel"
        Action   = "bedrock:InvokeModel"
        Effect   = "Allow"
        Resource = data.aws_bedrock_foundation_model.this.model_arn
      },
      {
        Sid      = "ApplyGuardrail"
        Action   = "bedrock:ApplyGuardrail"
        Effect   = "Allow"
        Resource = awscc_bedrock_guardrail_version.this.guardrail_arn
      }
    ]
  })
}

resource "awscc_bedrock_agent" "this" {
  agent_name              = "MyAgent"
  agent_resource_role_arn = aws_iam_role.bedrock_agent_this.arn
  auto_prepare            = true
  description             = "My Agent"
  foundation_model        = data.aws_bedrock_foundation_model.this.model_id
  guardrail_configuration = {
    guardrail_identifier = awscc_bedrock_guardrail_version.this.guardrail_arn
    guardrail_version    = awscc_bedrock_guardrail_version.this.version
  }
  instruction = "You are an assistant that provides information about world history. You are allowed to use general knowledge that you already possess to answer any history-related questions."
}

As you can see, the Terraform configuration maintains the familiar usage of resources and data sources in the AWS Provider. A quick validation in the Amazon Bedrock console shows that the agent does indeed filter the violent event about Julius Caesar, while it correctly answers another question about the history of wheels.

Testing the agent with guardrail

Summary

In this blog post, we looked at the pros and cons of the AWS Provider and the AWS CC Provider. As it stands, there is still a long way until AWS CC Provider has the quality and feature parity necessary to replace the AWS Provider, so both are here to stay for the foreseeable future.

If you're managing complex AWS infrastructure, now is the time to experiment with both providers. Use the AWS CC Provider for cutting-edge features, and rely on the AWS Provider for tried-and-true solutions. By blending both providers, you’ll have the best of both worlds in your Terraform configurations. You can follow this tutorial or find more information here.

For other tips and walkthroughs on AWS and Terraform, be sure to check out the Avangards Blog. Thanks for reading!