Terraform AWS providerの補完に！AWS Cloud Control providerの概要と使い方

# はじめに
- Terraformを扱っている方ならいつも触ることになるであろう、Terraform Registryのサイト。
- こちらには、扱えるProvider情報(どのクラウド/SaaSに対応しているか)や、対応しているリソース毎のTerraformコードの書き方なんかが乗っています。
- ちょっとこのProviderリストに注目していただきたいのですが、、
  - [Providers](https://registry.terraform.io/browse/providers)
- よく使用されるということで大きなアイコンで表現されているのは我らがAWSを含む5つのクラウド+Kubernetesです。
- しかし、少し下の方を見ると、実はもう一つ、AWS Cloud Controlというものがここにあることに気が付いていた方はいらっしゃるのでしょうか。
  ![registory_awscc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276335/20c33f20-9faa-48ec-bc0f-e08ad495b36d.png)

- かくいう私は、画面上部にでかでかとあるAWSの文字だけしか見ておらず、他にAWSのアイコンがあることに全く気が付いていませんでした。
- 先日(2025.3.13)、JAWS-UG東京のIaC Nightというイベントに参加してきまして、その中でこのAWS Cloud Controlが紹介されていましたので、軽くその紹介内容を振り返りつつ、触ってみようと思います。
  - [JAWS-UG東京 IaC Night 〜入門から上級まで！AWSをコードで構築しよう〜](https://jawsug.connpass.com/event/344954/)

:::note alert
筆者はAWS Cloud Controlを触ったことがなく、間違った記載をするかもしれませんが、その際は優しくご指摘いただけますと幸いです。
また、公式のドキュメントを読み込ませつつ、Claude 3.7 Sonnetを用いた解説を使用している部分があります。
:::

# AWS Cloud Control API
## AWS Cloud Control APIとは
- AWS Cloud Control APIは、AWSおよびサードパーティサービスに属するクラウドリソースを作成、読み取り、更新、削除、一覧表示（CRUD-L）するための標準化されたインターフェースを提供するサービスです。
## 主な特徴
- 統一されたAPI
  - 個々のサービス固有のAPIを学ぶことなく、一貫した方法でさまざまなAWSリソースを管理できます。
- 標準化された操作
  - 異なるリソースタイプに対して同じCRUD-L操作を実行できます。
- サードパーティ統合
  - AWSリソースだけでなく、サードパーティが提供するリソースタイプもサポートします。
- 既存リソースの操作
  - Cloud Control APIで作成されていないリソースも管理できます。
## 主要な操作
### リソース操作
- create-resource
  - リソースを作成します
- read-resource (または get-resource)
  - リソースの情報を取得します
- update-resource
  - 既存のリソースを更新します
- delete-resource
  - リソースを削除します
- list-resources
  - 特定のタイプのリソースを一覧表示します
### リクエスト操作
- cancel-resource-request
  - 進行中のリソース操作をキャンセルします
- get-resource-request-status
  - リソース操作リクエストのステータスを確認します
- list-resource-requests
  - アクティブなリソース操作リクエストを一覧表示します
## S3バケット操作の例
### S3バケットの作成
- コマンド

    ```shell
    aws cloudcontrol create-resource \
        --type-name AWS::S3::Bucket \
        --desired-state '{
            "BucketName": "cloud-control-example-bucket",
            "PublicAccessBlockConfiguration": {
                "BlockPublicAcls": true,
                "BlockPublicPolicy": true,
                "IgnorePublicAcls": true,
                "RestrictPublicBuckets": true
            }
        }'
    ```

- 実行例

    ```shell
    {
        "ProgressEvent": {
            "TypeName": "AWS::S3::Bucket",
            "Identifier": "cloud-control-example-bucket",
            "RequestToken": "64c94082-3dbd-42fd-a5ea-7f652c76c3fe",
            "Operation": "CREATE",
            "OperationStatus": "IN_PROGRESS",
            "EventTime": "2025-03-15T16:19:48.676000+00:00"
        }
    }
    ```

- 作成されたS3バケットの確認
  ![awscc_s3_create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276335/96f8aa04-9be0-4a9b-8013-0acc1b2eb56b.png)
- 作成されたS3バケットのパブリックアクセス設定の確認
  ![awscc_s3_access.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1276335/ac98dbbb-1759-4e70-8e0a-db71cfeda652.png)

### リクエストの状態確認
- コマンド

    ```shell
    aws cloudcontrol get-resource-request-status \
        --request-token 64c94082-3dbd-42fd-a5ea-7f652c76c3fe
    ```

- 実行例

    ```shell
    {
        "ProgressEvent": {
            "TypeName": "AWS::S3::Bucket",
            "Identifier": "cloud-control-example-bucket",
            "RequestToken": "64c94082-3dbd-42fd-a5ea-7f652c76c3fe",
            "Operation": "CREATE",
            "OperationStatus": "SUCCESS",
            "EventTime": "2025-03-15T16:20:01.525000+00:00"
        }
    }
    ```

- "CREATE"の"Operation"が"SUCCESS"となっていることからも、APIコールが成功していることがわかります。
### 作成したS3バケット情報の取得
- コマンド

    ```shell
    aws cloudcontrol get-resource \
        --type-name AWS::S3::Bucket \
        --identifier cloud-control-example-bucket
    ```

- 実行例

    ```json
    {
        "TypeName": "AWS::S3::Bucket",
        "ResourceDescription": {
            "Identifier": "cloud-control-example-bucket",
            "Properties": "{\"PublicAccessBlockConfiguration\":{\"RestrictPublicBuckets\":true,\"BlockPublicPolicy\":true,\"BlockPublicAcls\":true,\"IgnorePublicAcls\":true},\"BucketName\":\"cloud-control-example-bucket\",\"RegionalDomainName\":\"cloud-control-example-bucket.s3.ap-northeast-1.amazonaws.com\",\"OwnershipControls\":{\"Rules\":[{\"ObjectOwnership\":\"BucketOwnerEnforced\"}]},\"DomainName\":\"cloud-control-example-bucket.s3.amazonaws.com\",\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":false,\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"AES256\"}}]},\"WebsiteURL\":\"http://cloud-control-example-bucket.s3-website-ap-northeast-1.amazonaws.com\",\"DualStackDomainName\":\"cloud-control-example-bucket.s3.dualstack.ap-northeast-1.amazonaws.com\",\"Arn\":\"arn:aws:s3:::cloud-control-example-bucket\"}"
        }
    }
    ```

### S3バケットにタグを追加
- コマンド

    ```shell
    aws cloudcontrol update-resource \
        --type-name AWS::S3::Bucket \
        --identifier cloud-control-example-bucket \
        --patch-document '[
            {
            "op": "add",
            "path": "Tags",
            "value": [
                {
                "Key": "Creator",
                "Value": "CloudControl"
                }
            ]
            }
        ]'
    ```

- 実行例

    ```shell
    {
        "ProgressEvent": {
            "TypeName": "AWS::S3::Bucket",
            "Identifier": "cloud-control-example-bucket",
            "RequestToken": "4f7e4412-2c19-43d9-9fa7-4539182a013a",
            "Operation": "UPDATE",
            "OperationStatus": "IN_PROGRESS",
            "EventTime": "2025-03-15T16:29:04.270000+00:00",
            "ResourceModel": "{\"PublicAccessBlockConfiguration\":{\"RestrictPublicBuckets\":true,\"BlockPublicPolicy\":true,\"BlockPublicAcls\":true,\"IgnorePublicAcls\":true},\"BucketName\":\"cloud-control-example-bucket\",\"OwnershipControls\":{\"Rules\":[{\"ObjectOwnership\":\"BucketOwnerEnforced\"}]},\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":false,\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"AES256\"}}]},\"Tags\":[{\"Value\":\"CloudControl\",\"Key\":\"Creator\"}]}"
        }
    }
    ```

### 再度作成したS3バケット情報の取得
- コマンド

    ```shell
    aws cloudcontrol get-resource \
        --type-name AWS::S3::Bucket \
        --identifier cloud-control-example-bucket
    ```

- 実行例

    ```json
    {
        "TypeName": "AWS::S3::Bucket",
        "ResourceDescription": {
            "Identifier": "cloud-control-example-bucket",
            "Properties": "{\"PublicAccessBlockConfiguration\":{\"RestrictPublicBuckets\":true,\"BlockPublicPolicy\":true,\"BlockPublicAcls\":true,\"IgnorePublicAcls\":true},\"BucketName\":\"cloud-control-example-bucket\",\"RegionalDomainName\":\"cloud-control-example-bucket.s3.ap-northeast-1.amazonaws.com\",\"OwnershipControls\":{\"Rules\":[{\"ObjectOwnership\":\"BucketOwnerEnforced\"}]},\"DomainName\":\"cloud-control-example-bucket.s3.amazonaws.com\",\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":false,\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"AES256\"}}]},\"WebsiteURL\":\"http://cloud-control-example-bucket.s3-website-ap-northeast-1.amazonaws.com\",\"DualStackDomainName\":\"cloud-control-example-bucket.s3.dualstack.ap-northeast-1.amazonaws.com\",\"Arn\":\"arn:aws:s3:::cloud-control-example-bucket\",\"Tags\":[{\"Value\":\"CloudControl\",\"Key\":\"Creator\"}]}"
        }
    }
    ```

- 以下部分を見ると、タグ情報が追加されていることがわかりますね。

    ```
    "Tags\":[{\"Value\":\"CloudControl\",\"Key\":\"Creator\"}]
    ```

### アカウント内のS3バケット一覧を取得
- コマンド

    ```shell
    aws cloudcontrol list-resources --type-name AWS::S3::Bucket
    ```

- 実行例

    ```shell
    {
        "ResourceDescriptions": [
            {
                "Identifier": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "Properties": "{\"BucketName\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}"
            },
            {
                "Identifier": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "Properties": "{\"BucketName\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}"
            },
            {
                "Identifier": "cloud-control-example-bucket",
                "Properties": "{\"BucketName\":\"cloud-control-example-bucket\"}"
            },
            {
                "Identifier": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
                "Properties": "{\"BucketName\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"}"
            }
        ],
        "TypeName": "AWS::S3::Bucket"
    }
    ```

### S3バケットの削除
- コマンド

    ```shell
    aws cloudcontrol delete-resource \
        --type-name AWS::S3::Bucket \
        --identifier cloud-control-example-bucket
    ```

- 実行例

    ```shell
        {
            "ProgressEvent": {
                "TypeName": "AWS::S3::Bucket",
                "Identifier": "cloud-control-example-bucket",
                "RequestToken": "8f5990cb-3d43-41c7-9509-d4aede21c267",
                "Operation": "DELETE",
                "OperationStatus": "IN_PROGRESS",
                "EventTime": "2025-03-15T16:32:42.688000+00:00"
            }
        }
    ```

### 削除リクエストの完了確認
- コマンド

    ```shell
    aws cloudcontrol get-resource-request-status \
        --request-token 8f5990cb-3d43-41c7-9509-d4aede21c267
    ```

- 実行例

    ```shell
        {
            "ProgressEvent": {
                "TypeName": "AWS::S3::Bucket",
                "Identifier": "cloud-control-example-bucket",
                "RequestToken": "8f5990cb-3d43-41c7-9509-d4aede21c267",
                "Operation": "DELETE",
                "OperationStatus": "SUCCESS",
                "EventTime": "2025-03-15T16:32:43.427000+00:00"
            }
        }
    ```

- 作成の時と同様に、"DELETE"の"Operation"が"SUCCESS"となっていることからも、APIコールが成功していることがわかります。
## つまりどういうこと？
- AWS Cloud Control APIとは、[aws cli](https://aws.amazon.com/jp/what-is/cli/)を含めてある程度定型になっているAWS操作をより定型的に操作ができるようになったAPI(コマンド)です。
- AWS Cloud Control APIを使うことで、より直観的にAWS操作ができるようになります。
- コマンドは以下の形式

    ```
    aws cloudcontrol <リソース操作オプション> --type-name AWS::S3::<リソース種別>
    ```

# Terraform AWS Cloud Control provider(AWS CC Provider)
- ようやく本題です。
## 前提
- AWSは200以上のサービスを提供し、2023年には3,400以上の新機能をリリースするほど急速な機能提供を行っています。
- しかし、この機能提供の速度がかなり早く、あくまで3rd PartyのOSSであるTerraformが追いつけなくなっている、、ということも間々あります。
  - といっても、機能にもよりますが、数週間～1-2か月では適応しているような気がしますが。
- そんな時にリリースされたのがTerraform AWS Cloud Control providerです。
## Terraform AWS Cloud Control providerとは
- AWS Cloud Control APIを活用して、Amazon EC2インスタンスやAmazon S3バケットのような何百ものAWSリソースタイプのサポートを自動的に生成しています。
- AWS Cloud Control APIはCloudFormationリソースプロバイダースキーマに基づいています。
  - これによって、CloudFormationでサポートされているリソースがCloud Control APIでも利用可能になる、という仕組みです。
- AWS Cloud Control providerは **自動的** に生成されるため、AWS上の新しい機能やサービスがAWS Cloud Control APIで利用可能になるとすぐに対応することができ、既存のTerraform AWS標準プロバイダーのギャップに対応することができます。
  - この **自動的** というのがポイントです。
- この自動化されたプロセスにより、AWS Cloud Control providerはコミュニティが新しいサービスごとにスキーマやリソースの実装を作成するのを待つ必要がないため、新しいリソースをより早く提供することができるようになります。
- [現在AWS Cloud Control providerは1000を超えるAWSリソースとデータソースをサポートしており](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/guides/using-aws-with-awscc-provider)、AWSサービスチームがAWS Cloud Control API標準を採用し続けるにつれて、さらにサポートが追加されています。
- 現時点ではプロバイダーは毎週リリースされ、前週にAWSから収集した追加情報をまとめられています。
  - つまり、プロバイダーでサービスが表示されるまで最大 1 週間待つことになります。
  - [Launch Schedule](https://github.com/hashicorp/terraform-provider-awscc/blob/main/contributing/FAQ.md#launch-schedule)
## AWS providerとの違い
- AWS providerは人による修正/デプロイが行われているのに対し、AWS Cloud Control providerは前述のとおり自動化されたリリースがされています。
- そのため、AWS Cloud Control providerはAWS providerに比べてより早いというのが、AWS Cloud Control providerのメリットになります。
## 今後の使い分け
- AWS Cloud Control providerが新しくリリースされたということは、AWS Cloud Control providerのみが今後サポートされ、AWS providerはサポートされないということなのか？と疑問に思う方もいらっしゃるかもしれませんが、それは違います。
- 筆者の主観ですが、基本はAWS providerを使用し、新機能を使うときにAWS providerが対応していないという場合にAWS Cloud Control providerを使用することになるんじゃないか、と思います。
- もちろん、AWS Cloud Control providerで作成されたリソースをAWS providerにimportすることが可能となっています。
# AWS Cloud Control providerを使ってみる
- AWS Cloud Control APIで作成したように、S3周りで機能を試してみます。
## AWS Cloud Control providerでS3バケットを作成
- [awscc_s3_bucket (Resource)](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/s3_bucket)
- 使用したコード

    ```hcl
    terraform {
        required_providers {
            awscc = {
            source = "hashicorp/awscc"
            version = "1.33.0"
            }
        }
    }

    provider "awscc" {
        region = "ap-northeast-1"
    }

    resource "awscc_s3_bucket" "example" {
        bucket_name = "awscc-s3-bucket"

        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
    }
    ```

- 実行例(init)
    <details><summary>awsccでS3バケットを作成するinitコマンド実行結果</summary>

    ```shell
    $ terraform init
    Initializing the backend...
    Initializing provider plugins...
    - Finding hashicorp/awscc versions matching "1.33.0"...
    - Installing hashicorp/awscc v1.33.0...
    - Installed hashicorp/awscc v1.33.0 (signed by HashiCorp)
    Terraform has created a lock file .terraform.lock.hcl to record the provider
    selections it made above. Include this file in your version control repository
    so that Terraform can guarantee to make the same selections by default when
    you run "terraform init" in the future.

    Terraform has been successfully initialized!

    You may now begin working with Terraform. Try running "terraform plan" to see
    any changes that are required for your infrastructure. All Terraform commands
    should now work.

    If you ever set or change modules or backend configuration for Terraform,
    rerun this command to reinitialize your working directory. If you forget, other
    commands will detect it and remind you to do so if necessary.
    $ 
    ```

    </details>
- 実行例(plan)
    <details><summary>awsccでS3バケットを作成するplanコマンド実行結果</summary>

    ```shell
    $ terraform plan
    
    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    + create

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be created
    + resource "awscc_s3_bucket" "example" {
        + accelerate_configuration           = (known after apply)
        + access_control                     = (known after apply)
        + analytics_configurations           = (known after apply)
        + arn                                = (known after apply)
        + bucket_encryption                  = (known after apply)
        + bucket_name                        = "awscc-s3-bucket"
        + cors_configuration                 = (known after apply)
        + domain_name                        = (known after apply)
        + dual_stack_domain_name             = (known after apply)
        + id                                 = (known after apply)
        + intelligent_tiering_configurations = (known after apply)
        + inventory_configurations           = (known after apply)
        + lifecycle_configuration            = (known after apply)
        + logging_configuration              = (known after apply)
        + metadata_table_configuration       = (known after apply)
        + metrics_configurations             = (known after apply)
        + notification_configuration         = (known after apply)
        + object_lock_configuration          = (known after apply)
        + object_lock_enabled                = (known after apply)
        + ownership_controls                 = (known after apply)
        + public_access_block_configuration  = {
            + block_public_acls       = true
            + block_public_policy     = true
            + ignore_public_acls      = true
            + restrict_public_buckets = true
            }
        + regional_domain_name               = (known after apply)
        + replication_configuration          = (known after apply)
        + tags                               = (known after apply)
        + versioning_configuration           = (known after apply)
        + website_configuration              = (known after apply)
        + website_url                        = (known after apply)
        }

    Plan: 1 to add, 0 to change, 0 to destroy.

    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

    Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
    you run "terraform apply" now.
    $ 
    ```

    </details>
- 実行例(apply)
    <details><summary>awsccでS3バケットを作成するapplyコマンド実行結果</summary>

    ```shell
    $ terraform apply

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    + create

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be created
    + resource "awscc_s3_bucket" "example" {
        + accelerate_configuration           = (known after apply)
        + access_control                     = (known after apply)
        + analytics_configurations           = (known after apply)
        + arn                                = (known after apply)
        + bucket_encryption                  = (known after apply)
        + bucket_name                        = "awscc-s3-bucket"
        + cors_configuration                 = (known after apply)
        + domain_name                        = (known after apply)
        + dual_stack_domain_name             = (known after apply)
        + id                                 = (known after apply)
        + intelligent_tiering_configurations = (known after apply)
        + inventory_configurations           = (known after apply)
        + lifecycle_configuration            = (known after apply)
        + logging_configuration              = (known after apply)
        + metadata_table_configuration       = (known after apply)
        + metrics_configurations             = (known after apply)
        + notification_configuration         = (known after apply)
        + object_lock_configuration          = (known after apply)
        + object_lock_enabled                = (known after apply)
        + ownership_controls                 = (known after apply)
        + public_access_block_configuration  = {
            + block_public_acls       = true
            + block_public_policy     = true
            + ignore_public_acls      = true
            + restrict_public_buckets = true
            }
        + regional_domain_name               = (known after apply)
        + replication_configuration          = (known after apply)
        + tags                               = (known after apply)
        + versioning_configuration           = (known after apply)
        + website_configuration              = (known after apply)
        + website_url                        = (known after apply)
        }

    Plan: 1 to add, 0 to change, 0 to destroy.

    Do you want to perform these actions?
    Terraform will perform the actions described above.
    Only 'yes' will be accepted to approve.

    Enter a value: yes

    awscc_s3_bucket.example: Creating...
    awscc_s3_bucket.example: Still creating... [10s elapsed]
    awscc_s3_bucket.example: Still creating... [20s elapsed]
    awscc_s3_bucket.example: Creation complete after 22s [id=awscc-s3-bucket]

    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    $ 
    ```

    </details>
## 作成したS3バケット情報の取得
- 実行例(show)
    <details><summary>terraform showによる作成リソース確認</summary>

    ```shell
    $ terraform show
    # awscc_s3_bucket.example:
    resource "awscc_s3_bucket" "example" {
        arn                               = "arn:aws:s3:::awscc-s3-bucket"
        bucket_encryption                 = {
            server_side_encryption_configuration = [
                {
                    bucket_key_enabled                = false
                    server_side_encryption_by_default = {
                        sse_algorithm = "AES256"
                    }
                },
            ]
        }
        bucket_name                       = "awscc-s3-bucket"
        domain_name                       = "awscc-s3-bucket.s3.amazonaws.com"
        dual_stack_domain_name            = "awscc-s3-bucket.s3.dualstack.ap-northeast-1.amazonaws.com"
        id                                = "awscc-s3-bucket"
        ownership_controls                = {
            rules = [
                {
                    object_ownership = "BucketOwnerEnforced"
                },
            ]
        }
        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
        regional_domain_name              = "awscc-s3-bucket.s3.ap-northeast-1.amazonaws.com"
        website_url                       = "http://awscc-s3-bucket.s3-website-ap-northeast-1.amazonaws.com"
    }
    $
    ```

    </details>
## S3バケットにタグを追加
- 修正したコード

    ```hcl
    terraform {
        required_providers {
            awscc = {
            source = "hashicorp/awscc"
            version = "1.33.0"
            }
        }
    }

    provider "awscc" {
        region = "ap-northeast-1"
    }

    resource "awscc_s3_bucket" "example" {
        bucket_name = "awscc-s3-bucket"
        
        # 追加部分
        tags = [{
            key   = "Name"
            value = "awsccbucket"
        }]

        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
    }
    ```

- 実行例(plan)
    <details><summary>awsccでS3バケットを編集するplanコマンド実行結果</summary>

    ```shell
    $ terraform plan
    awscc_s3_bucket.example: Refreshing state... [id=awscc-s3-bucket]

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    ~ update in-place

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be updated in-place
    ~ resource "awscc_s3_bucket" "example" {
        + accelerate_configuration           = (known after apply)
        + access_control                     = (known after apply)
        + analytics_configurations           = (known after apply)
        + cors_configuration                 = (known after apply)
            id                                 = "awscc-s3-bucket"
        + intelligent_tiering_configurations = (known after apply)
        + inventory_configurations           = (known after apply)
        + lifecycle_configuration            = (known after apply)
        + logging_configuration              = (known after apply)
        + metadata_table_configuration       = (known after apply)
        + metrics_configurations             = (known after apply)
        + notification_configuration         = (known after apply)
        + object_lock_configuration          = (known after apply)
        + object_lock_enabled                = (known after apply)
        + replication_configuration          = (known after apply)
        + tags                               = [
            + {
                + key   = "Name"
                + value = "awsccbucket"
                },
            ]
        + versioning_configuration           = (known after apply)
        + website_configuration              = (known after apply)
            # (9 unchanged attributes hidden)
        }

    Plan: 0 to add, 1 to change, 0 to destroy.

    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

    Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
    you run "terraform apply" now.
    $ 
    ```

    </details>
- 実行例(apply)
    <details><summary>awsccでS3バケットを編集するapplyコマンド実行結果</summary>

    ```shell
    $ terraform apply
    awscc_s3_bucket.example: Refreshing state... [id=awscc-s3-bucket]

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    ~ update in-place

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be updated in-place
    ~ resource "awscc_s3_bucket" "example" {
        + accelerate_configuration           = (known after apply)
        + access_control                     = (known after apply)
        + analytics_configurations           = (known after apply)
        + cors_configuration                 = (known after apply)
            id                                 = "awscc-s3-bucket"
        + intelligent_tiering_configurations = (known after apply)
        + inventory_configurations           = (known after apply)
        + lifecycle_configuration            = (known after apply)
        + logging_configuration              = (known after apply)
        + metadata_table_configuration       = (known after apply)
        + metrics_configurations             = (known after apply)
        + notification_configuration         = (known after apply)
        + object_lock_configuration          = (known after apply)
        + object_lock_enabled                = (known after apply)
        + replication_configuration          = (known after apply)
        + tags                               = [
            + {
                + key   = "Name"
                + value = "awsccbucket"
                },
            ]
        + versioning_configuration           = (known after apply)
        + website_configuration              = (known after apply)
            # (9 unchanged attributes hidden)
        }

    Plan: 0 to add, 1 to change, 0 to destroy.

    Do you want to perform these actions?
    Terraform will perform the actions described above.
    Only 'yes' will be accepted to approve.

    Enter a value: yes

    awscc_s3_bucket.example: Modifying... [id=awscc-s3-bucket]
    awscc_s3_bucket.example: Still modifying... [id=awscc-s3-bucket, 10s elapsed]
    awscc_s3_bucket.example: Still modifying... [id=awscc-s3-bucket, 20s elapsed]
    awscc_s3_bucket.example: Still modifying... [id=awscc-s3-bucket, 30s elapsed]
    awscc_s3_bucket.example: Modifications complete after 32s [id=awscc-s3-bucket]

    Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
    $ 
    ```

    </details>
- 実行例(show)
    <details><summary>terraform showによる編集リソース確認</summary>

    ```shell
    $ terraform show
    # awscc_s3_bucket.example:
    resource "awscc_s3_bucket" "example" {
        arn                               = "arn:aws:s3:::awscc-s3-bucket"
        bucket_encryption                 = {
            server_side_encryption_configuration = [
                {
                    bucket_key_enabled                = false
                    server_side_encryption_by_default = {
                        sse_algorithm = "AES256"
                    }
                },
            ]
        }
        bucket_name                       = "awscc-s3-bucket"
        domain_name                       = "awscc-s3-bucket.s3.amazonaws.com"
        dual_stack_domain_name            = "awscc-s3-bucket.s3.dualstack.ap-northeast-1.amazonaws.com"
        id                                = "awscc-s3-bucket"
        ownership_controls                = {
            rules = [
                {
                    object_ownership = "BucketOwnerEnforced"
                },
            ]
        }
        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
        regional_domain_name              = "awscc-s3-bucket.s3.ap-northeast-1.amazonaws.com"
        tags                              = [
            {
                key   = "Name"
                value = "awsccbucket"
            },
        ]
        website_url                       = "http://awscc-s3-bucket.s3-website-ap-northeast-1.amazonaws.com"
    }
    $ 
    ```

    </details>
## S3バケットの削除
- 実行例(destroy)
    <details><summary>awsccでS3バケットを削除するdestroyコマンド実行結果</summary>

    ```shell
    $ terraform destroy
    awscc_s3_bucket.example: Refreshing state... [id=awscc-s3-bucket]

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    - destroy

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be destroyed
    - resource "awscc_s3_bucket" "example" {
        - arn                               = "arn:aws:s3:::awscc-s3-bucket" -> null
        - bucket_encryption                 = {
            - server_side_encryption_configuration = [
                - {
                    - bucket_key_enabled                = false -> null
                    - server_side_encryption_by_default = {
                        - sse_algorithm = "AES256" -> null
                        } -> null
                    },
                ] -> null
            } -> null
        - bucket_name                       = "awscc-s3-bucket" -> null
        - domain_name                       = "awscc-s3-bucket.s3.amazonaws.com" -> null
        - dual_stack_domain_name            = "awscc-s3-bucket.s3.dualstack.ap-northeast-1.amazonaws.com" -> null
        - id                                = "awscc-s3-bucket" -> null
        - ownership_controls                = {
            - rules = [
                - {
                    - object_ownership = "BucketOwnerEnforced" -> null
                    },
                ] -> null
            } -> null
        - public_access_block_configuration = {
            - block_public_acls       = true -> null
            - block_public_policy     = true -> null
            - ignore_public_acls      = true -> null
            - restrict_public_buckets = true -> null
            } -> null
        - regional_domain_name              = "awscc-s3-bucket.s3.ap-northeast-1.amazonaws.com" -> null
        - tags                              = [
            - {
                - key   = "Name" -> null
                - value = "awsccbucket" -> null
                },
            ] -> null
        - website_url                       = "http://awscc-s3-bucket.s3-website-ap-northeast-1.amazonaws.com" -> null
        }

    Plan: 0 to add, 0 to change, 1 to destroy.

    Do you really want to destroy all resources?
    Terraform will destroy all your managed infrastructure, as shown above.
    There is no undo. Only 'yes' will be accepted to confirm.

    Enter a value: yes

    awscc_s3_bucket.example: Destroying... [id=awscc-s3-bucket]
    awscc_s3_bucket.example: Destruction complete after 5s

    Destroy complete! Resources: 1 destroyed.
    $ 
    ```

    </details>
## AWS Cloud Control providerで作成したリソースをAWS providerにimport
### Step1. AWS Cloud Control providerでS3バケットを作成
- 使用したコード

    ```hcl
    terraform {
        required_providers {
            awscc = {
            source = "hashicorp/awscc"
            version = "1.33.0"
            }
        }
    }

    provider "awscc" {
        region = "ap-northeast-1"
    }

    resource "awscc_s3_bucket" "example" {
        bucket_name = "awscc-s3-bucket"
        
        tags = [{
            key   = "Name"
            value = "awsccbucket"
        }]

        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
    }
    ```

- 実行例(plan)
    <details><summary>terraform planによる作成リソース確認</summary>

    ```shell
    $ terraform plan

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    + create

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be created
    + resource "awscc_s3_bucket" "example" {
        + accelerate_configuration           = (known after apply)
        + access_control                     = (known after apply)
        + analytics_configurations           = (known after apply)
        + arn                                = (known after apply)
        + bucket_encryption                  = (known after apply)
        + bucket_name                        = "awscc-s3-bucket"
        + cors_configuration                 = (known after apply)
        + domain_name                        = (known after apply)
        + dual_stack_domain_name             = (known after apply)
        + id                                 = (known after apply)
        + intelligent_tiering_configurations = (known after apply)
        + inventory_configurations           = (known after apply)
        + lifecycle_configuration            = (known after apply)
        + logging_configuration              = (known after apply)
        + metadata_table_configuration       = (known after apply)
        + metrics_configurations             = (known after apply)
        + notification_configuration         = (known after apply)
        + object_lock_configuration          = (known after apply)
        + object_lock_enabled                = (known after apply)
        + ownership_controls                 = (known after apply)
        + public_access_block_configuration  = {
            + block_public_acls       = true
            + block_public_policy     = true
            + ignore_public_acls      = true
            + restrict_public_buckets = true
            }
        + regional_domain_name               = (known after apply)
        + replication_configuration          = (known after apply)
        + tags                               = [
            + {
                + key   = "Name"
                + value = "awsccbucket"
                },
            ]
        + versioning_configuration           = (known after apply)
        + website_configuration              = (known after apply)
        + website_url                        = (known after apply)
        }

    Plan: 1 to add, 0 to change, 0 to destroy.

    ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

    Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
    you run "terraform apply" now.
    $ 
    ```

    </details>
- 実行例(apply)
    <details><summary>terraform applyによるリソース作成</summary>

    ```shell
    $ terraform apply

    Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
    following symbols:
    + create

    Terraform will perform the following actions:

    # awscc_s3_bucket.example will be created
    + resource "awscc_s3_bucket" "example" {
        + accelerate_configuration           = (known after apply)
        + access_control                     = (known after apply)
        + analytics_configurations           = (known after apply)
        + arn                                = (known after apply)
        + bucket_encryption                  = (known after apply)
        + bucket_name                        = "awscc-s3-bucket"
        + cors_configuration                 = (known after apply)
        + domain_name                        = (known after apply)
        + dual_stack_domain_name             = (known after apply)
        + id                                 = (known after apply)
        + intelligent_tiering_configurations = (known after apply)
        + inventory_configurations           = (known after apply)
        + lifecycle_configuration            = (known after apply)
        + logging_configuration              = (known after apply)
        + metadata_table_configuration       = (known after apply)
        + metrics_configurations             = (known after apply)
        + notification_configuration         = (known after apply)
        + object_lock_configuration          = (known after apply)
        + object_lock_enabled                = (known after apply)
        + ownership_controls                 = (known after apply)
        + public_access_block_configuration  = {
            + block_public_acls       = true
            + block_public_policy     = true
            + ignore_public_acls      = true
            + restrict_public_buckets = true
            }
        + regional_domain_name               = (known after apply)
        + replication_configuration          = (known after apply)
        + tags                               = [
            + {
                + key   = "Name"
                + value = "awsccbucket"
                },
            ]
        + versioning_configuration           = (known after apply)
        + website_configuration              = (known after apply)
        + website_url                        = (known after apply)
        }

    Plan: 1 to add, 0 to change, 0 to destroy.

    Do you want to perform these actions?
    Terraform will perform the actions described above.
    Only 'yes' will be accepted to approve.

    Enter a value: yes

    awscc_s3_bucket.example: Creating...
    awscc_s3_bucket.example: Still creating... [10s elapsed]
    awscc_s3_bucket.example: Still creating... [20s elapsed]
    awscc_s3_bucket.example: Creation complete after 29s [id=awscc-s3-bucket]

    Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
    $ 
    ```

    </details>
### Step2. AWS provider側のS3バケットにインポート
- 修正したコード

    ```hcl
    terraform {
        required_providers {
            awscc = {
                source  = "hashicorp/awscc"
                version = "1.33.0"
            }
            # 追加したAWS provider
            aws = {
                source = "hashicorp/aws"
                version = "5.91.0"
            }
        }
    }

    provider "awscc" {
        region = "ap-northeast-1"
    }

    # 追加したAWS provider
    provider "aws" {
        region = "ap-northeast-1"
    }

    # 既存のAWSCCリソース
    resource "awscc_s3_bucket" "example" {
        bucket_name = "awscc-s3-bucket"
        
        tags = [{
            key   = "Name"
            value = "awsccbucket"
        }]

        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
    }

    # AWS provider側のS3バケット定義
    resource "aws_s3_bucket" "example" {
        bucket = "awscc-s3-bucket"
        
        tags = {
            Name = "awsccbucket"
        }
    }

    # AWS provider側のパブリックアクセスブロック設定
    resource "aws_s3_bucket_public_access_block" "example" {
        bucket = aws_s3_bucket.example.id

        block_public_acls       = true
        block_public_policy     = true
        ignore_public_acls      = true
        restrict_public_buckets = true
    }
    ```

- 実行例(init)
  - AWS providerを使うため、再度initが必要になります。
    <details><summary>AWS providerをinit</summary>

    ```shell
    $ terraform init
    Initializing the backend...
    Initializing provider plugins...
    - Reusing previous version of hashicorp/awscc from the dependency lock file
    - Finding hashicorp/aws versions matching "5.91.0"...
    - Using previously-installed hashicorp/awscc v1.33.0
    - Installing hashicorp/aws v5.91.0...
    - Installed hashicorp/aws v5.91.0 (signed by HashiCorp)
    Terraform has made some changes to the provider dependency selections recorded
    in the .terraform.lock.hcl file. Review those changes and commit them to your
    version control system if they represent changes you intended to make.

    Terraform has been successfully initialized!

    You may now begin working with Terraform. Try running "terraform plan" to see
    any changes that are required for your infrastructure. All Terraform commands
    should now work.

    If you ever set or change modules or backend configuration for Terraform,
    rerun this command to reinitialize your working directory. If you forget, other
    commands will detect it and remind you to do so if necessary.
    $ 
    ```

    </details> 
- 実行例(import)
    <details><summary>AWS provider側にS3バケットとパブリックアクセスブロック設定をインポート</summary>

    ```shell
    $ terraform import aws_s3_bucket.example awscc-s3-bucket
    aws_s3_bucket.example: Importing from ID "awscc-s3-bucket"...
    aws_s3_bucket.example: Import prepared!
    Prepared aws_s3_bucket for import
    aws_s3_bucket.example: Refreshing state... [id=awscc-s3-bucket]

    Import successful!

    The resources that were imported are shown above. These resources are now in
    your Terraform state and will henceforth be managed by Terraform.

    $ 
    $ terraform import aws_s3_bucket_public_access_block.example awscc-s3-bucket
    aws_s3_bucket_public_access_block.example: Importing from ID "awscc-s3-bucket"...
    aws_s3_bucket_public_access_block.example: Import prepared!
    Prepared aws_s3_bucket_public_access_block for import
    aws_s3_bucket_public_access_block.example: Refreshing state... [id=awscc-s3-bucket]

    Import successful!

    The resources that were imported are shown above. These resources are now in
    your Terraform state and will henceforth be managed by Terraform.

    $ 
    ```

    </details>
- 実行例(show)
  - 下記の通り、AWS providerとAWS Cloud Control providerの両方に、同じS3バケットが登録されていることがわかります。
    <details><summary>S3バケットの設定を確認</summary>

    ```shell
    $ terraform show
    # aws_s3_bucket.example:
    resource "aws_s3_bucket" "example" {
        acceleration_status         = null
        arn                         = "arn:aws:s3:::awscc-s3-bucket"
        bucket                      = "awscc-s3-bucket"
        bucket_domain_name          = "awscc-s3-bucket.s3.amazonaws.com"
        bucket_prefix               = null
        bucket_regional_domain_name = "awscc-s3-bucket.s3.ap-northeast-1.amazonaws.com"
        hosted_zone_id              = "Z2M4EHUR26P7ZW"
        id                          = "awscc-s3-bucket"
        object_lock_enabled         = false
        policy                      = null
        region                      = "ap-northeast-1"
        request_payer               = "BucketOwner"
        tags                        = {
            "Name" = "awsccbucket"
        }
        tags_all                    = {
            "Name" = "awsccbucket"
        }

        grant {
            id          = "ee7cb282c8e05b7266fdc3df7a6c430c6ed53e3515d3950a1365f19d43d35bf0"
            permissions = [
                "FULL_CONTROL",
            ]
            type        = "CanonicalUser"
            uri         = null
        }

        server_side_encryption_configuration {
            rule {
                bucket_key_enabled = false

                apply_server_side_encryption_by_default {
                    kms_master_key_id = null
                    sse_algorithm     = "AES256"
                }
            }
        }

        versioning {
            enabled    = false
            mfa_delete = false
        }
    }

    # aws_s3_bucket_public_access_block.example:
    resource "aws_s3_bucket_public_access_block" "example" {
        block_public_acls       = true
        block_public_policy     = true
        bucket                  = "awscc-s3-bucket"
        id                      = "awscc-s3-bucket"
        ignore_public_acls      = true
        restrict_public_buckets = true
    }

    # awscc_s3_bucket.example:
    resource "awscc_s3_bucket" "example" {
        arn                               = "arn:aws:s3:::awscc-s3-bucket"
        bucket_encryption                 = {
            server_side_encryption_configuration = [
                {
                    bucket_key_enabled                = false
                    server_side_encryption_by_default = {
                        sse_algorithm = "AES256"
                    }
                },
            ]
        }
        bucket_name                       = "awscc-s3-bucket"
        domain_name                       = "awscc-s3-bucket.s3.amazonaws.com"
        dual_stack_domain_name            = "awscc-s3-bucket.s3.dualstack.ap-northeast-1.amazonaws.com"
        id                                = "awscc-s3-bucket"
        ownership_controls                = {
            rules = [
                {
                    object_ownership = "BucketOwnerEnforced"
                },
            ]
        }
        public_access_block_configuration = {
            block_public_acls       = true
            block_public_policy     = true
            ignore_public_acls      = true
            restrict_public_buckets = true
        }
        regional_domain_name              = "awscc-s3-bucket.s3.ap-northeast-1.amazonaws.com"
        tags                              = [
            {
                key   = "Name"
                value = "awsccbucket"
            },
        ]
        website_url                       = "http://awscc-s3-bucket.s3-website-ap-northeast-1.amazonaws.com"
    }
    $ 
    ```

    </details>
### Step3. AWS Cloud Control providerを削除し、AWS providerに統一
- 実行例(state rm)
    <details><summary>AWS Cloud Control providerのS3バケットを状態管理から削除(実際のS3バケットは削除されません)</summary>

    ```shell
    $ terraform state rm awscc_s3_bucket.example
    Removed awscc_s3_bucket.example
    Successfully removed 1 resource instance(s).
    $ 
    ```

    </details>
- 修正したコード

    ```hcl
    terraform {
        required_providers {
            aws = {
                source = "hashicorp/aws"
                version = "5.91.0"
            }
        }
    }

    provider "aws" {
        region = "ap-northeast-1"
    }

    resource "aws_s3_bucket" "example" {
        bucket = "awscc-s3-bucket"
        
        tags = {
            Name = "awsccbucket"
        }
    }

    resource "aws_s3_bucket_public_access_block" "example" {
        bucket = aws_s3_bucket.example.id

        block_public_acls       = true
        block_public_policy     = true
        ignore_public_acls      = true
        restrict_public_buckets = true
    }
    ```

- 実行例(plan)
    <details><summary>AWS Cloud Control providerから除外した後の確認(plan)</summary>

    ```shell
    $ terraform plan
    aws_s3_bucket.example: Refreshing state... [id=awscc-s3-bucket]
    aws_s3_bucket_public_access_block.example: Refreshing state... [id=awscc-s3-bucket]

    No changes. Your infrastructure matches the configuration.

    Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are
    needed.
    $ 
    ```

    </details>
- 実行例(show)
    <details><summary>AWS Cloud Control providerから除外した後の確認(show)</summary>

    ```shell
    $ terraform show
    # aws_s3_bucket.example:
    resource "aws_s3_bucket" "example" {
        acceleration_status         = null
        arn                         = "arn:aws:s3:::awscc-s3-bucket"
        bucket                      = "awscc-s3-bucket"
        bucket_domain_name          = "awscc-s3-bucket.s3.amazonaws.com"
        bucket_prefix               = null
        bucket_regional_domain_name = "awscc-s3-bucket.s3.ap-northeast-1.amazonaws.com"
        hosted_zone_id              = "Z2M4EHUR26P7ZW"
        id                          = "awscc-s3-bucket"
        object_lock_enabled         = false
        policy                      = null
        region                      = "ap-northeast-1"
        request_payer               = "BucketOwner"
        tags                        = {
            "Name" = "awsccbucket"
        }
        tags_all                    = {
            "Name" = "awsccbucket"
        }

        grant {
            id          = "ee7cb282c8e05b7266fdc3df7a6c430c6ed53e3515d3950a1365f19d43d35bf0"
            permissions = [
                "FULL_CONTROL",
            ]
            type        = "CanonicalUser"
            uri         = null
        }

        server_side_encryption_configuration {
            rule {
                bucket_key_enabled = false

                apply_server_side_encryption_by_default {
                    kms_master_key_id = null
                    sse_algorithm     = "AES256"
                }
            }
        }

        versioning {
            enabled    = false
            mfa_delete = false
        }
    }

    # aws_s3_bucket_public_access_block.example:
    resource "aws_s3_bucket_public_access_block" "example" {
        block_public_acls       = true
        block_public_policy     = true
        bucket                  = "awscc-s3-bucket"
        id                      = "awscc-s3-bucket"
        ignore_public_acls      = true
        restrict_public_buckets = true
    }
    $ 
    ```

    </details>
# まとめ
- AWS Cloud Control providerはAWS Cloud Control APIを元にしたTerraform用のproviderです。
- 標準的に利用されてきたAWS providerに比べて、AWS Cloud Control providerはリリース頻度が高いため、新機能対応も早いようです。
- AWS Cloud Control providerもAWS providerと大きく使い勝手変わらず使えそうなことがわかりました。
  - どちらかで作成したリソースをもう片方にimportするということも可能です。
- AWS Cloud Control providerだけ使う、、ということは無く、AWS Cloud Control providerもAWS providerも仲良く使い分けしていくことが今後必要そうです。
  - 使い分けの例として、本番利用はAWS providerを、新機能を試すときはAWS Cloud Control providerで作成しておき、AWS providerがその新機能に対応したらAWS providerにimportするという案が考えられます。
# 追記
- HashiCorp社公式から、AWS ProviderとAWS CC Providerについて解説する記事がリリースされました
  - [AWS and AWSCC Terraform providers: Better together](https://www.hashicorp.com/en/blog/aws-and-awscc-terraform-providers-better-together)
# 参照
- [What is AWS Cloud Control API?](https://docs.aws.amazon.com/cloudcontrolapi/latest/userguide/what-is-cloudcontrolapi.html)
- [Quickly adopt new AWS features with the Terraform AWS Cloud Control provider](https://aws.amazon.com/jp/blogs/devops/quickly-adopt-new-aws-features-with-the-terraform-aws-cloud-control-provider/)
- [Terraform Registry Providers](https://registry.terraform.io/browse/providers)
- [Terraform Registry awscc Providers](https://registry.terraform.io/providers/hashicorp/awscc/latest)
- [Terraform Registry aws Providers](https://registry.terraform.io/providers/hashicorp/aws/latest)
- [Frequently Asked Questions of awscc](https://github.com/hashicorp/terraform-provider-awscc/blob/main/contributing/FAQ.md#launch-schedule)