TerraformのクラウドコントロールAPI専用のproviderを使ってみた

2021.10.31

2021年9月30日にクラウドコントロールAPIがGA(一般提供開始)しました。

従来、各サービスごとにバラバラだったAPIの使い方を、一貫した方法で使えるようにしたAPIです。またAWSサービスのみならず CloudFormation Registry で利用可能なサードパーティー製ソリューションもこのAPIを通して利用可能です。

前回、このクラウドコントロールAPIをTerraformのaws provider経由で使ってみました。

今回はTerraform経由でのもう一つの使い方をご紹介します。クラウドコントロールAPI専用のprovider「awscc」がリリースされていますのでこれを使ってみます。

なおこのproviderは現在tech previewです。本番環境での利用はまだ控えておきましょう。

使ってみた

前回と同様、このproviderを使って2AZにそれぞれPublicとPrivateサブネットを持つような一般的なVPC構成を作ってみたいと思います。

Terraformとproviderの定義

terraform {
  required_version = "1.0.10"

  required_providers {
    awscc = {
      source  = "hashicorp/awscc"
      version = "0.4.0"
    }
  }
}
provider "awscc" {
  region = "ap-northeast-1"
}

普通のproviderの設定ですね。

認証

認証はaws providerと同様、多様な方法で可能です。アクセスキー・シークレットキーを providerブロックのattributeで渡す、環境変数にセットする、profile attributeを使う、AWS_PROFILE環境変数を使うなど。 AWS SSO Profileも使えました。

まずはVPCだけ

こんな感じのコードです。

resource "awscc_ec2_vpc" "vpc" {
  cidr_block           = "192.168.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  instance_tenancy     = "default"
  tags = [{
    key   = "Name"
    value = "basic-vpc"
  }]
}

前回試したaws providerでクラウドコントロールAPIを使う例では、どんなリソースを作る場合でもaws_cloudcontrolapi_resource を使い、そのtype_name値を切り替えることでプロビジョニングするリソースを切り替えていました。が、この awscc providerはリソースごとにResourceが定義されています。

aws providerのaws_cloudcontrolapi_resourceでVPCをプロビジョニングする例1

resource "aws_cloudcontrolapi_resource" "vpc" {
  type_name = "AWS::EC2::VPC"
  desired_state = jsonencode({
    CidrBlock          = "192.168.0.0/16"
    EnableDnsSupport   = true
    EnableDnsHostnames = true
    InstanceTenancy    = "default"
    Tags = [{
      Key   = "Name"
      Value = "plain-vpc"
    }]
  })
}

aws providerのaws_cloudcontrolapi_resourceでVPCをプロビジョニングする例2

resource "aws_cloudcontrolapi_resource" "vpc" {
  type_name = "AWS::EC2::VPC"
  desired_state = jsonencode(yamldecode(
    <<-EOT
    CidrBlock: "192.168.0.0/16"
    EnableDnsSupport: true
    EnableDnsHostnames: true
    InstanceTenancy: default
    Tags:
      - Key: Name
        Value: "plain-vpc"
    EOT
  ))
}

aws_cloudcontrolapi_resourceはリソースのプロパティを desired_state attribute以下にJSON型で押し込む形になっています。そのためjsonencode()などを挟まないと書きにくいです。この点awsccはより簡潔に書けて良いと思います。

以下はaws providerのaws_vpcリソースを使った、つまり一般的なTerraformでのVPCの書き方ですが、ほとんど前述のawsccのコードと同じですよね。

resource "aws_vpc" "vpc" {
  cidr_block           = "192.168.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  instance_tenancy     = "default"
  tags = {
    Name = "plain-vpc"
  }
}

他のリソースも

全体のコードは以下のようになりました。前回aws providerのaws_cloudcontrolapi_resourceでやったときと同様、一部リソースがクラウドコントロールAPIでは未対応だったのでその部分はaws providerを使っています。

terraform {
  required_version = "1.0.10"

  required_providers {
    awscc = {
      source  = "hashicorp/awscc"
      version = "0.4.0"
    }
    aws = {
      source  = "hashicorp/aws"
      version = "3.63.0"
    }
  }
}
provider "awscc" {
  region = "ap-northeast-1"
}
provider "aws" {
  region = "ap-northeast-1"
}

resource "awscc_ec2_vpc" "vpc" {
  cidr_block           = "192.168.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  instance_tenancy     = "default"
  tags = [{
    key   = "Name"
    value = "basic-vpc"
  }]
}
# public subnet
resource "aws_internet_gateway" "igw" {
  vpc_id = awscc_ec2_vpc.vpc.id
  tags = {
    Name = "basic-vpc-igw"
  }
}
data "aws_availability_zones" "az" {}
resource "aws_subnet" "public" {
  count                   = 2
  vpc_id                  = awscc_ec2_vpc.vpc.id
  cidr_block              = cidrsubnet(awscc_ec2_vpc.vpc.cidr_block, 8, count.index)
  availability_zone       = data.aws_availability_zones.az.names[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "basic-vpc-public-subnet-${count.index + 1}"
  }
}
resource "awscc_ec2_route_table" "public" {
  vpc_id = awscc_ec2_vpc.vpc.id
  tags = [{
    key   = "Name"
    value = "basic-vpc-public-subnet-rtb"
  }]
}
resource "aws_route" "public" {
  route_table_id         = awscc_ec2_route_table.public.id
  gateway_id             = aws_internet_gateway.igw.id
  destination_cidr_block = "0.0.0.0/0"
}
resource "awscc_ec2_subnet_route_table_association" "public" {
  count          = 2
  route_table_id = awscc_ec2_route_table.public.id
  subnet_id      = aws_subnet.public.*.id[count.index]
}

# private subnet
resource "aws_subnet" "private" {
  count                   = 2
  vpc_id                  = awscc_ec2_vpc.vpc.id
  cidr_block              = cidrsubnet(awscc_ec2_vpc.vpc.cidr_block, 8, count.index + 2)
  availability_zone       = data.aws_availability_zones.az.names[count.index]
  map_public_ip_on_launch = false

  tags = {
    Name = "basic-vpc-private-subnet-${count.index + 1}"
  }
}
resource "awscc_ec2_route_table" "private" {
  vpc_id = awscc_ec2_vpc.vpc.id
  tags = [{
    key   = "Name"
    value = "basic-vpc-private-subnet-rtb"
  }]
}
resource "awscc_ec2_subnet_route_table_association" "private" {
  count          = 2
  route_table_id = awscc_ec2_route_table.private.id
  subnet_id      = aws_subnet.private.*.id[count.index]
}

使ってみた感想

先に書いたとおり、aws providerのaws_cloudcontrolapi_resourceよりは書きやすい、読みやすいと思います。が、クラウドコントロールAPIの一番のユースケースは、aws providerで未対応のリソースをプロビジョニングしたいとき、です。この観点で言うとaws_cloudcontrolapi_resourceに軍配が上がるかなと思います。aws_cloudcontrolapi_resourceは該当リソースがクラウドコントロールAPIに対応してさえいればすぐ使うことができます。対してこのawscc providerはproviderのバージョンアップで対応リソースが増えない限りは使うことができません。どんどん対応リソースは増えているようですが…

将来的に、awsccでプロビジョニングできるリソースの種類がaws providerと同じくらいになったら、awsccが覇権を握る未来が来るかもしれません。

参考情報