HashiCorp社によるAWS providerとAWSCC providerの解説記事を翻訳&解説

# はじめに
- 先月、[Terraform AWS providerの補完に！AWS Cloud Control providerの概要と使い方](https://qiita.com/ec2_on_aws/items/fd90ce364e87a132a9a8)という記事を作成し、AWS用のTerraform providerはAWS provider以外にもAWS Cloud Control providerというものもありますよ、と紹介しました
- そんな時、Terraform公式のHashiCorp社から、[AWS and AWSCC Terraform providers: Better together](https://www.hashicorp.com/ja/blog/aws-and-awscc-terraform-providers-better-together)という記事がリリースされていました
- この記事では、上記HashiCorp社のブログを翻訳しつつ、筆者の理解をコメントしていきます

# メイン
- AWS および AWSCC providerを使用してクラウド インフラストラクチャを管理し、provider間で状態を移動する方法に関する戦略を確認します

- Terraformには2つのAWSproviderがあります
    - AWSとAWSCCの2つです
    - 1つは手書きで、もう1つは自動生成されています
    - しかし、なぜ違いがあるのでしょうか？この投稿では、以下について詳しく説明します
        - これらのアプローチの背景にある理由
        - 各providerがAWS APIとどのように相互作用するか
        - それらが競合するのではなく、互いに補完し合う方法
    - また、それらを一緒に使用するためのベストプラクティスや、リソースを再作成することなく、あるproviderから別のproviderへ移行する方法についても共有します

## AWS provider
- Terraform AWS providerは、AWS統合のための最初の公式Terraform providerであり、AWS SDKを基に構築されています
- このproviderは2024年に10周年を迎え、30億ダウンロードを超えました
- 様々なAWSサービス用のリソースが含まれており、それぞれが対応するサービスへのAPIコールを行います
- 例えば、TerraformがAmazon S3バケットを作成する場合、AWS providerはS3 CreateBucket APIを呼び出します

![AWS provider の図示](https://www.hashicorp.com/_next/image?url=https%3A%2F%2Fwww.datocms-assets.com%2F2885%2F1745506749-aws-provider.png&w=3840&q=75)

- AWS providerのすべてのリソース（例えばaws_s3_bucketリソースなど）がGoで手書きされていることに注目することは重要です
- これにより厳格なテストカバレッジが確保される一方、優先順位付けと手作業の労力が必要なため、すべてのAWSリソースをサポートすること、特に新しいサービスの初日サポートは課題となります

### コメント
- Terraformの標準で利用されてきたAWS providerはAWS SDKを用いて構築されていることをご存じな方は、あくまで感覚ですがそれほど多くないように思います
- 以下はよくワードとして出てきますが、前二つに比べてAWS SDKはそれほど知名度と意図的な使用は少ないのではないでしょうか
    - AWS CLI
    - AWS CDK
    - AWS SDK
- AWS SDKってどういう時に使うの？そう、Terraformの中で使うんです

- そんなAWS providerですが、現在11周年と長く使われています
- 30億DLは、私のようなテスト利用もあるでしょうが、それでも恐ろしい数です

- また、AWS providerはGo言語で**手書き**で記載されているため、厳密なテストが行われたのちリリースされるというメリットがある一方、手書きのため、特に新サービスのリリースには時間がかかってしまうというデメリットがあります

## AWS Cloud Control API
- AWSはCloud Control APIを提供しています
- これは、AWSの機能やサービスにプログラムでアクセスできるようにするサービスで、多くの場合、それらが立ち上げられた当日からアクセス可能です
- Cloud Control APIは最新のAWSリソースに対応し続けており、開発者が自分のソリューションをCloud Control APIと一度統合するだけで、追加の統合作業を行うことなく、新しいAWSサービスや機能に自動的にアクセスできるようにします
- さらに、直感的で分かりやすい方法でサービスを管理するための標準化されたCRUDL API一式を開発者に提供します

### コメント
- これはTerraformにおけるproviderの話題から一度離れ、AWSのAPIの話に移ります
- Cloud Control API は、これらのリソースにアクセスしてプロビジョニングする標準化された方法を提供することで、 AWS アカウントのリソースを一貫して制御できます
- AWS アカウントで利用可能なさまざまなリソースタイプに直接呼び出しを行うための統一されたプログラムインターフェイスを提供します
    - 詳細は[とは AWS クラウドコントロール API](https://docs.aws.amazon.com/ja_jp/cloudcontrolapi/latest/userguide/what-is-cloudcontrolapi.html)も併せてご参照ください
- この**統一されたプログラムインターフェイス**というのが、この後ご紹介する**AWSCC provider**をリリースすることができた背景だと考えています

- また**CRUDL**とは、データベースやAPIで一般的に使われる基本的な操作の頭文字をとったものです
    - 具体的には、Create（作成）、Read（読み込み）、Update（更新）、Delete（削除）、List（一覧表示）を意味します
    - これらの操作は、AWSのリソースを管理する際の基本的なアクションであり、AWS Cloud Control APIではこれらの操作を統一的なインターフェースで提供していますというものです
## AWSCC provider
- このCloud Control APIの一貫性により、AWSとHashiCorpは2024年5月に一般提供となったAWSCC providerを開発することができました
- その統一されたインターフェースにより、新しいリソーススキーマがリリースされるとすぐに、コード化されたルールに基づいて新しいリソースを自動的に生成することができます
- AWS providerと同様に、AWSCC providerもAWS APIと相互作用します
- しかし、違いは、AWSCCがリソースを作成する際に、AWS Cloud Control APIとのみ相互作用することです

![AWSCC provider の図示](https://www.hashicorp.com/_next/image?url=https%3A%2F%2Fwww.datocms-assets.com%2F2885%2F1745506793-awscc-provider.png&w=3840&q=75)

- 各リソースは同じCloud Control APIと相互作用し、5つのCRUDL操作（作成、読み取り、更新、削除、リスト）の全部または一部をサポートします
- これにより、特にエアギャップ環境においてインフラストラクチャをデプロイするために必要なインターフェースエンドポイントの数を減らすという追加の利点ももたらします
- Cloud Control APIは統合されたインターフェースエンドポイントを提供します
- さらに、AWSCCはCloud Control APIによって新しい互換性のあるサービスがサポートされると自動的に生成されるため、新しいAWSサービスや機能に対して多くの場合、発売日サポートを提供します
- AWS providerにも同様のサポートを追加することを目指していますが、AWSCC providerはその生成プロセスのため、最初にそれらをサポートする可能性が高いです

### コメント
- AWSCC providerはちょうど一年前、2024年5月にリリースされ、1周年を迎えました
    - めでたいですね
- 前述の通り、**統一されたプログラムインターフェイス**を採用することで、新しいリソースへの対応が自動的にできるようになりました
- AWS providerはAWS APIに対応しているのに対し、AWSCC providerはAWS Cloud Control APIにのみ対応しています
- AWSCC providerの一番のメリットは、**新しいAWSサービスや機能に対して多くの場合、発売日サポートを提供すること**です
    - 具体的にどれくらい早いの？だとか、そのAWS providerとの時間差を調査すると、**日本で採用するかどうか**が見えてきます
    - この観点については今後調査できれば、、と考えています
- また、**エアギャップ環境**とは、セキュリティを高めるために物理的にインターネットやその他の外部ネットワークから完全に切り離されたコンピュータネットワーク環境のことです

## Better together: Using both the AWS and AWSCC provider
- AWSCC providerは、標準のAWS providerを使用した既存のTerraform構成に追加するための優れた補完的 providerです
- AWS providerは、約200のサービスにわたる1,300以上のリソースタイプに対して最高のユーザーエクスペリエンスとパフォーマンスを提供します
- AWSCC providerは、AWSによって公開されたCloud Control APIから生成された最新のAWSサービスへのアクセスを提供することでこれを拡張します
- AWSCC providerとAWS providerを一緒に使用することで、開発者は確立されたものから新しいものまで、AWSサービス全体の大規模なリソースカタログを利用できるようになります

### コメント
- AWSCC providerはAWS providerと喧嘩するためにリリースされたproviderではありません
- AWS providerを補完するためにAWSCC providerはリリースされています

## Using the AWSCC provider
- AWSCC providerを使用して単純なVPCをデプロイするには、以下の構成例を使用できます
- 開発者エクスペリエンスは標準のAWS providerを使用する場合と非常に似ています
- 主な違いは、リソースとデータソースの名前が若干異なる命名規則に従っていることです

```hcl
provider "awscc" {
    region = "us-east-1"
}

resource "awscc_ec2_vpc" "this" {
    cidr_block = "10.0.0.0/16"
}
```

### コメント
- これはAWSCC providerを用いて、VPCを作成する、一番シンプルな設定ファイルです
- providerの表記はAWS providerと差異はありません
    - regionは重要です
- resource名は"awscc"という接頭辞が付くことが特徴です
    - この接頭辞を元に、AWS providerとAWSCC providerを区別することができます

## Using the AWSCC provider with the AWS provider
- 以下の例では、AWS providerは単純なVPCの作成に使用され、一方AWSCC providerはそのVPC内にサブネットを作成します

```hcl
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"
}
```

### コメント
- 前述AWSCC providerでVPCを作成する設定ファイルに、AWS providerを追加しています
- また、AWSCC providerで作成したVPCのIDを参照してAWS providerでsubnetを作成しています
    ```
    vpc_id      = awscc_ec2_vpc.this.id
    ```
- このように、AWSCC providerとAWS providerを連携させることができます

## Migrating a resource from AWSCC to the AWS provider
- 以下のコードを出発点として使用してください
- これはAWSCC providerを使用してVPCを作成し、AWS providerを使用してサブネットを作成します
- "terraform init"と"terraform apply --auto-approve"を実行してください

```hcl
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"
}
```

- VPCをAWSCC providerからAWS providerに移行するには、上記のコードを以下のコードに置き換え、次にvpc-exampleIDを前のステップからのVPC IDに置き換えてください
- その後、"terraform apply --auto-approve"を実行します
- 実際の環境では、すべてのステップを開発環境でテストし、"terraform plan"でTerraformプランを確認してから、Terraformの実行を承認すべきであることに注意してください

```hcl
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"
}
```

- 上記のコードで行われた変更点は以下の通りです：
    1. awscc_ec2_vpcをaws_vpcにリファクタリングし、aws_subnetのvpc_id引数をaws_vpcに向けます。VPCのAWSCCリソースとAWSリソースの両方が同じcidr_block引
    数を使用するため、VPCの引数にそれ以上の変更はありません

    ```
    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. aws_vpcをインポートするためのimportブロックを追加します

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

    3. 状態からawscc_ec2_vpcを削除するためのremovedブロックを追加します

    ```hcl
    removed {
        from = awscc_ec2_vpc.this

        lifecycle {
            destroy = false
        }
    }
    ```

### コメント
- 最初の設定ファイルは前章と同じです
- このAWSCC providerとAWS providerが両立しているところからAWS providerのみに移行する手順が紹介されています
- 移行するには、AWS providerにimportするコードとAWSCC providerから削除するコードが必要になります
- 特に注意する必要があるのは削除のコードです
    - リソースは削除せず、あくまで状態(state)から削除することで、AWS providerでも利用を継続することができます
    ```
    「lifecycle」ブロックは必須です
    「destroy」引数は、Terraformがリソースによって管理されているオブジェクトを破棄しようとするかどうかを決定します
    「false」という値は、Terraformがオブジェクトを破棄せずに、状態からリソースを削除することを意味します
    ```
    - [Removing Resources](https://developer.hashicorp.com/terraform/language/resources/syntax#removing-resources)

- また、以下は重要なコメントです
    - 実際の環境=本番環境で誤ってリソースを削除しないよう注意しましょう

```
実際の環境では、すべてのステップを開発環境でテストし、"terraform plan"でTerraformプランを確認してから、Terraformの実行を承認すべきであることに注意してください
```

## Migrating a resource from AWS to the AWSCC provider
- 以下のコードを出発点として使用してください
- これはAWSCC providerを使用してVPCを作成し、AWS providerを使用してサブネットを作成します
- "terraform init"と"terraform apply --auto-approve"を実行してください

    ```hcl
    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"
    }
    ```

- AWS providerからAWSCC providerにサブネットを移行するには、上記のコードを以下のコードに置き換え、前のステップで作成されたサブネットのIDでsubnet-exampleIDを置き換えてください
- その後、"terraform apply --auto-approve"を実行します
- 実際の環境では、すべてのステップを開発環境でテストし、"terraform plan"でTerraformプランを確認してから、Terraformの実行を承認すべきであることに注意してください

    ```hcl
    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"
    }
    ```

- 上記のコードで行われた変更点は以下の通りです：
    1. aws_subnetをawscc_ec2_subnetにリファクタリングします。サブネットのAWSCCリソースとAWSリソースの両方が同じvpc_idとcidr_block引数を使用するため、VPCの引数にそれ以上の変更はありません
    
    ```hcl
    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. awscc_ec2_subnetをインポートするためのimportブロックを追加します
    ```hcl
    import {
        to = awscc_ec2_subnet.this
        id = "subnet-exampleID"
    }
    ```
    3. 状態からaws_subnetを削除するためのremovedブロックを追加します
    ```hcl
    removed {
        from = aws_subnet.this

        lifecycle {
            destroy = false
        }
    }
    ```

### コメント
- この章では、AWS providerで作成したリソース(aws_subnet)を、AWSCC providerのリソース(awscc_ec2_subnet)に移行する手順を紹介しています
- と言いつつ、AWSCC providerからAWS providerに移行する手順と何も変わりません
- AWSCC providerにimportするコードとAWS providerから削除するコードを用いることで移行が完了します
- ただ、、AWSCC providerにまとめる事、あるのでしょうか？？という疑問はあります

## Learn more
- AWS providerとAWSCC providerを一緒に使用する方法についての詳細なリソースについては、Terraformでクラウドリソースを管理するためのこの[AWSワークショップ](https://catalog.workshops.aws/manage-cloud-resources-with-terraform/en-US)を参照してください
- provider間での状態の移行例や遭遇した注意点についてさらに詳しく知りたい場合は、provider間の状態移行に関するこの[HashiTalk](https://www.youtube.com/watch?v=LdAglxXbFH8)を参照してください

# おわりに
- AWS providerとAWSCC providerの違い、そしてそれぞれでの移行方法について学ぶことができました
- 1つの設定ファイルで複数のクラウドを操作する(マルチクラウド)場合、複数のproviderが混在することはしょうがないですが、1つのクラウドを操作するだけであれば複数のProviderを混在させることは複雑さを招くだけです
- そのため、一時的にAWS providerとAWSCC providerの2つが存在することはやむなしですが、ゆくゆくはどちらかに移行することが望ましいように思います
- そうすると、、、例えば以下の方針で統合していくのではないでしょうか
    - 新しいサービス/新機能がリリースされたら一度AWSCC providerでリソースを作成し、AWS providerに上記が追加されたのち、**使い慣れた**AWS providerに移行する
    - **新しいサービス/新機能はAWSCC providerの方が先に追加される**という前提で、AWSCC providerだけを使うようにする
- と、そんなことを言っても、こと日本という国の風潮として、どうしても枯れた技術/サービスの方が尊ばれる傾向にあると思います
- そのため、AWS providerが対応してないレベルのリリース状態のものを果たして採用することがあるのか？という強い疑問が出てきます
- 結局はAWS providerのみを使用し続け、新機能/サービスがAWS providerに対応して**しばらくしてから**採用する、ということになるんじゃないでしょうか