GoogleCloudのTerraformエクスポート機能を試す

# はじめに
* gcloudコマンドで[実機からterraformコードを生成する機能](https://cloud.google.com/docs/terraform/resource-management/export)がPreviewで利用可能になっているため試してみました

# 使ってみる
### terraformコードを生成する

##### 事前準備
プロジェクトで[Cloud Asset Inventry APIを有効化](https://cloud.google.com/asset-inventory/docs/export-asset-metadata?hl=ja)します
* 複数プロジェクトを利用している場合、`gcloud config set project ${PROJECT_ID}` で設定したプロジェクトで有効化されていれば出力対象のプロジェクトで有効化する必要はありません

##### コードを出力する
`${project_id}`にはterraformコードを生成したいプロジェクトのIDを、`${output_path}`には出力したいフォルダのパスを設定してください
```shell
% gcloud beta resource-config bulk-export --project=${project_id} --resource-format=terraform --path=${output_path}
Exporting resource configurations to [./]...done. 
Exported 206 resource configuration(s) to [./].
```
上記の場合(206 Resource configurations)、約7分程で出力が完了しました
`--project`を`--folder`や`--organization`にすることで、フォルダ・組織単位で出力することもできます

##### 出力を確認してみる
以下のようにリソース種別やリージョン・ゾーンごとにtfファイルがフォルダ分けされた状態でtfファイルが生成されます
※出力内容は作成されているリソースによって変わります

* ディレクトリ構成
※プロジェクトID/プロジェクト番号/フォルダ番号は編集しています
```shell
% tree -d
.
├── ${PROJECT_NUMBER}
│   ├── ${PROJECT_NUMBER}
│   │   └── Project
│   │       └── LoggingLogSink
│   └── Service
├── ${FOLDER_NUMBER}
│   └── Project
├── projects
│   ├── ${PROJECT_NUMBER}
│   │   └── SecretManagerSecret
│   └── ${PROJECT_ID}
│       ├── ArtifactRegistryRepository
│       │   ├── asia
│       │   └── asia-northeast1
│       ├── BigQueryTable
│       ├── ComputeAddress
│       │   ├── asia-northeast1
│       │   └── global
│       ├── ComputeBackendBucket
│       ├── ComputeBackendService
│       │   └── global
│       ├── ComputeFirewall
│       ├── ComputeForwardingRule
│       │   └── global
│       ├── ComputeHealthCheck
│       │   └── global
│       ├── ComputeInstance
│       │   ├── asia-northeast1-a
│       │   └── asia-northeast1-b
│       ├── ComputeInstanceGroup
│       │   └── asia-northeast1-a
│       ├── ComputeInstanceTemplate
│       │   └── asia-northeast1
│       ├── ComputeNetwork
│       ├── ComputeNetworkEndpointGroup
│       │   └── asia-northeast1-a
│       ├── ComputeRoute
│       ├── ComputeRouter
│       │   └── asia-northeast1
│       ├── ComputeSSLCertificate
│       │   └── global
│       ├── ComputeSecurityPolicy
│       ├── ComputeSubnetwork
│       │   └── asia-northeast1
│       ├── ComputeTargetHTTPProxy
│       │   └── global
│       ├── ComputeTargetHTTPSProxy
│       │   └── global
│       ├── ComputeURLMap
│       │   └── global
│       ├── ContainerCluster
│       │   ├── asia-northeast1-a
│       │   └── tmp-cluster
│       │       └── ContainerNodePool
│       │           └── asia-northeast1-a
│       ├── DNSManagedZone
│       ├── IAMServiceAccount
│       ├── KMSKeyRing
│       │   ├── asia-northeast1
│       │   └── locations
│       │       └── asia-northeast1
│       │           └── keyRings
│       │               └── tmp
│       │                   └── KMSCryptoKey
│       ├── MonitoringAlertPolicy
│       ├── PubSubSubscription
│       ├── PubSubTopic
│       ├── SQLInstance
│       │   └── asia-northeast1
│       └── StorageBucket
│           ├── ASIA
│           ├── ASIA-NORTHEAST1
│           └── US
└── ${PROJECT_ID}
    ├── BigQueryDataset
    │   ├── US
    │   └── asia-northeast1
    └── ComputeDisk
        ├── asia-northeast1-a
        ├── asia-northeast1-b
        └── asia-northeast1-c

81 directories
```

* ファイルの中身
    * 1リソース1ファイル作成されます
    * プロジェクトID修正しています
```shell
# 例(VPC)
% cat ./projects/${PROJECT_ID}/ComputeNetwork/tmp-vpc.tf                    
resource "google_compute_network" "tmp_vpc" {
  auto_create_subnetworks = false
  mtu                     = 1460
  name                    = "tmp-vpc"
  project                 = "${PROJECT_ID}"
  routing_mode            = "REGIONAL"
}
# terraform import google_compute_network.tmp_vpc projects/${PROJECT_ID}/global/networks/tmp-vpc
```


* デフォルト設定ではリソース種別やリージョン・ゾーンごとにtfファイルがフォルダ分けされますが、以下のように1つのtfファイルにまとめて出力することもできます
```shell
$ gcloud beta resource-config bulk-export --resource-format=terraform >> gcp_resources.tf
```
* 個別のリソース(GCEなど)に絞って出力することもできます
```shell
# Firewallのみ出力したい場合
$ gcloud beta resource-config bulk-export --resource-types=ComputeFirewall --project=${PROJECT_ID} --resource-format=terraform

# resource-typesは以下のKRM KINDの名前を指定します
% gcloud beta resource-config list-resource-types
┌──────────────────────────────────────┬──────────────┬─────────┬──────┐
│               KRM KIND               │ BULK EXPORT? │ EXPORT? │ IAM? │
├──────────────────────────────────────┼──────────────┼─────────┼──────┤
│ AccessContextManagerAccessLevel      │              │         │      │
│ AccessContextManagerAccessPolicy     │              │         │ x    │
│ AccessContextManagerServicePerimeter │              │         │      │
〜中略〜
│ StorageDefaultObjectAccessControl    │              │         │      │
│ StorageNotification                  │              │         │      │
│ StorageTransferJob                   │              │         │      │
└──────────────────────────────────────┴──────────────┴─────────┴──────┘

# resource-typesは複数指定することもできます
$ gcloud beta resource-config bulk-export --resource-types=ComputeFirewall,ComputeInstance --project=${PROJECT_ID} --resource-format=terraform

# resource-typesはテキストから読み込むこともできます
$ cat types.txt
ComputeFirewall
ComputeInstance

$ gcloud beta resource-config bulk-export --resource-types-file=types.tx --path=tf-output --project=${PROJECT_ID} --resource-format=terraform
```

### importコマンドを生成する
* コードを生成しただけではtfstateが空の状態であるため、importを行う必要がありますがこちらもgcloudコマンドで簡単に生成することができます。便利！
```shell
% gcloud beta resource-config terraform generate-import --output-script-file=import.sh --output-module-file=modules.tf ${output_path}
Generating import script....done.                                                                                                  
Successfully generated 
/tmp/import.sh with imports for 199 resources.
Generating terraform modules....done.
```
* 上記のコマンドを実行すると、importコマンドを実行するshell(例の場合、`import.sh`)と`modules.tf`が生成されます
* `modules.tf`を利用すると一括で`terraform plan/apply`ができるようにもなります

※生成された個々のterraformリソースにもコメントでimportコマンドが記載されているためリソース量が少ない場合はわざわざ生成しなくてもコメントのimportコマンドを利用できます

### tfstateをGCSで管理する
* 生成したterraformコードではbackendに関しては記載されないため、ローカルにtfstateファイルが保存されます
* 複数人で利用するケースなどではtfstateをリモートで管理したい場合は[こちら](https://cloud.google.com/docs/terraform/resource-management/store-state)を参考にGCSでバックエンドを作成することができます

# おわりに
* GoogleCloudをIaCで管理する際は、Terraformか[Deployment Manager](https://cloud.google.com/deployment-manager/docs/configuration?hl=ja)が利用できるため、Terraformを利用している方が多いのではないかと思います
* 実際にリソースを作成する際はいきなりIaCで記載するよりもConsole上でお試ししてからIaCを記載する流れで実施するケースも多くこのようなケースではコードを記載する時間を大幅に短縮できる便利な機能だと感じました
* 「類似リソースの作成はcountやfor_eachでやりたい」「リソース名等を変数化したい」といった場合には生成されたコードを修正する必要がありますが1からterraformコードをするよりコーディングが楽にできる印象です
* 類似機能で[Config Connectorにexportする機能](https://cloud.google.com/config-connector/docs/how-to/import-export/bulk-export?hl=ja)もあるのでこちらも試してみたいと思います

* 余談ですが、Terraformには[Terraform Associate](https://www.hashicorp.com/certification/terraform-associate)という資格があり、先日取得しました。こちらの受験記も後日書こうと思います
<img width="400" alt="HashiCorp_Certified__Terraform_Associate__002__Badge.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2876448/ac0415c2-a50e-4477-935d-2da3c900bd7a.png">