内容
これまで GCP を手動構築していた範囲を、Terraform で IaC 化したので、その内容を記載する
これまで手動(Webコンソール, gcloud, etc.)でやっていた内容 (Terraform コード化対象)
構築内容
前述の「これまでやっていた内容 (Terraform コード化対象)」のリンク先内容を実施する
内容は下記で、記載済み内容については説明を省略し、追加するものは補足を記載している箇所がある
- 組織の作成と共有 VPC 構築
- ホストプロジェクト作成
- サービスプロジェクト作成
- 共有 VPC
- Subnet の設定
- [追加] 組織のポリシー追加
- [追加] 権限の追加
- HA VPN
- 自宅ラボ向けに HA VPN を構築する
- 自宅ラボ向けに設定Config例を出力する (EdgeRouer向け)
- VPC Service Controls
- サービス境界の作成
- 限定公開 Google アクセス
- googleapis.com 向けの Private 向け DNS 設定
GCP リソース構成
組織の中にフォルダ(部署に当たる)を作成して、プロジェクトを所属構成にしている (記載方法参照)
terraform 用に管理プロジェクトを組織直に構築している (参考:https://cloud.google.com/community/tutorials/managing-gcp-projects-with-terraform)
プロジェクト名については、エンタープライズ向けガイドのプロジェクト名称を参照に、
プラスして自分のIDを追加して他と被らないようにした (IDはユニークである必要がある)
- プロジェクト名称(ID): [組織名]-[サービス名]-[環境名]
Terraform ディレクトリ構成
正解がわからなかったため、単純にファイル分割とした。(module 化などは後ほど検討したい.デファクトスタンダードはなさそう)
事前作業・Terraform 用プロジェクト作成については、別フォルダ (prestage) にした
すべてを Qiita に書くと長くなったので、省略記載して、全体は GitHub に公開した (URL:https://github.com/suzuyu/terraform-public)
※backend.tf
は prestage/ 実行での出力に沿って作成する(GitHubにはない)
.
├── README.md
├── backend.tf
├── cloudnat.tf
├── dns.tf
├── firewall.tf
├── host.tf
├── main.tf
├── organization.tf
├── prestage
│ ├── main.tf
│ ├── output.tf
│ ├── variables.tf
├── service1.tf
├── subnet.tf
├── terraform_pj
├── variables.tf
├── vpc_service_controls.tf
└── vpn.tf
以降、各ファイルごとに構築・記載内容を説明する
構築
下記順番で実施している
- 前準備 / ドメイン取得・CloudIdentity有効化(手動)
- terraform 管理プロジェクトの作成 (prestage/)
- 組織の設定 (organaization.tf)
- ホストプロジェクトの作成 (host.tf)
- サービスプロジェクトの作成 (service1.tf)
- サブネットの作成 (subnet.tf)
- ファイアウォールの設定 (firewall.tf)
- DNS の設定(限定公開 Google アクセス) (dns.tf)
- HA VPN の作成 (vpn.tf)
- インターネット向け NAT の設定 (cloudnat.tf)
- VPC Service Controls の作成 (vpc_service_controls.tf)
1. 前準備 / ドメイン取得・CloudIdentity有効化(手動)
ドメイン取得と組織の有効化までは、手動で実施
- ドメイン取得
- 組織の作成 (Cloud Identity, 組織の作成までを完了させる)
作業環境に、terraform のインストールと gcloud のインストールを実施しておく
% terraform version
Terraform v0.14.6
% gcloud --version
Google Cloud SDK 327.0.0
alpha 2021.02.05
beta 2021.02.05
bq 2.0.64
core 2021.02.05
gsutil 4.58
以降、terraform での構築を進める
2. terraform 管理プロジェクトの作成 (prestage/)
Google Cloud の Community にグーグル社員が書いたチュートリアルを参考に、Terraform 管理プロジェクトを作成する
チュートリアルでは手動で実施している箇所を prestage/
でterraformのコードにして実施する (手動でも良かったかもしれない)
Terraform 用のサービスアカウントは未作成のため、gcloud の認証情報で gcp へアクセスすることになる
Create the Terraform Admin Project
:https://cloud.google.com/community/tutorials/managing-gcp-projects-with-terraform
下記、上記URL の中で参照しコード化した内容
- Create the Terraform Admin Project
- terraform admin project の作成
- 組織配下の指定をする
- Create the Terraform service account
- terraform を動かすための service account を作成する
- prestage は gcloud の認証アカウントで動かすが、今後サーバ等で動かすことが可能になる
- service account の認証キー (json) はファイルとして出力し、その後の必要コマンドは output で出力する
- terraform で必要な API を有効化する
- Add organization/folder-level permissions
- terraform service account にプロジェクト作成権限と課金ユーザ権限などを付与する
- terraform state の保存先ストレージの作成 / Set up remote state in Cloud Storage
- terraform の backend にする Cloud Storage を作成する
- backend 設定の記載方法は output で出力する
├── prestage
│ ├── main.tf
│ ├── output.tf
│ ├── variables.tf
変数として、下記を準備する
コメントに書いてあるが、xxx
でマスクしてあるところを変更する
# org_id (下記で出力される "ID" を "org_id" の値にする)
## gcloud organizations list
# billing_account (下記で出力される "ACCOUNT_ID" を "billing_account" の値にする)
## gcloud beta billing accounts list
# gcp-terraform-admin@[xxx.xxx]
## 組織で使用するドメイン(xxx.xxx)の Cloud Identity で事前に Terraform 管理ユーザグループのアカウントグループを作成しておく(同じ権限にして切り分けよう)
# org_name
## 組織の識別子、プロジェクトの命名に必要なだけで何でも良い
variable "gcp_common" {
type = object({
org_name = string
org_id = string
billing_account = string
})
default = {
org_name = "xxxxxx"
org_id = "xxxxxxxxxxxx"
billing_account = "xxxxxx-xxxxxx-xxxxxx"
}
}
variable "admin_user_group" {
type = object({
email = string
})
default = {
email = "gcp-terraform-admin@[xxx.xxx]"
}
}
variable "terraform_pj" {
type = object({
identity_name = string
})
default = {
identity_name = "terraformadmin"
}
}
terraform での実行内容は下記main.tf
の通り
コメントに書いてあるが、事前作業として書きを実施する(実施コマンドはコメント参照)
- 課金アカウントへ権限を付与する
- 課金アカウント(
variables.tf
でbilling_account
)へ今回 gcloud を実施する組織管理者アカウントへ権限を付与する
- 課金アカウント(
- 組織管理者アカウントで gcloud 設定をする
- 組織ポリシーを操作できるように権限を付与する
- workspace の名前で環境名としてプロジェクトに名称付与するので、terraform workspace を切り替える
- workspace を使ったほうがいいかは悩みどころだが試しに使用してみている
## 内容
# Terraform 用のプロジェクトを作成する
# 参照 https://cloud.google.com/community/tutorials/managing-gcp-projects-with-terraform
## 前提
# 組織を作成済み
# 課金アカウント作成済み
# 管理ユーザグループを admin.google で作成済み
## 組織管理者が課金アカウントへ権限付与できるようにする
# gcloud config set account [課金アカウントのアドミンアカウント]
# gcloud beta billing accounts list
# gcloud beta billing accounts add-iam-policy-binding [var.gcp_common.billing_account] --member=user:[org admin user account] --role roles/billing.admin
## 組織管理者のアカウントで gcloud コマンドを利用できるようにする
# gcloud auth login [org admin user account]
# gcloud config set account [org admin user account]
# gcloud auth application-default login
## 組織管理者が組織ポリシーを編集できるようにする
# gcloud organizations list
# gcloud organizations add-iam-policy-binding [var.gcp_common.org_id] --member=user:[org admin user account] --role=roles/orgpolicy.policyAdmin
## workspace を "dev", "prd" などにする
# terraform workspace new dev
provider "google" {}
# Terraform Project 作成
resource "google_project" "terraform" {
name = join("-", [var.gcp_common.org_name, var.terraform_pj.identity_name, terraform.workspace])
project_id = join("-", [var.gcp_common.org_name, var.terraform_pj.identity_name, terraform.workspace])
org_id = var.gcp_common.org_id
billing_account = var.gcp_common.billing_account
auto_create_network = false
}
# Sevice API 有効化 (google_project と同じ terraform で実施が必須)
resource "google_project_service" "terraform" {
project = google_project.terraform.id
disable_dependent_services = true
for_each = toset([
"cloudresourcemanager.googleapis.com",
"serviceusage.googleapis.com",
"cloudidentity.googleapis.com",
"cloudbilling.googleapis.com",
"iam.googleapis.com",
"compute.googleapis.com",
"container.googleapis.com",
"accesscontextmanager.googleapis.com", # VPC Service Controls に必要
])
service = each.value
depends_on = [
google_project.terraform,
]
}
# Terraform サービスアカウントの作成
resource "google_service_account" "terraform" {
account_id = "terraform"
display_name = "Terraform IaC Account"
project = google_project.terraform.project_id
depends_on = [
google_project.terraform,
]
}
# Terraform へホストプロジェクトの閲覧ロールを付与
resource "google_project_iam_binding" "storage_serviceusage" {
project = google_project.terraform.project_id
for_each = toset([
"roles/storage.admin",
"roles/serviceusage.serviceUsageAdmin",
])
role = each.value
members = [
join(":", ["serviceAccount", google_service_account.terraform.email]),
join(":", ["group", var.admin_user_group.email]),
]
depends_on = [
google_service_account.terraform,
]
}
# Terraform へホストプロジェクトの閲覧ロールを付与
resource "google_project_iam_binding" "viewer" {
project = google_project.terraform.project_id
for_each = toset([
"roles/viewer",
])
role = each.value
members = [
join(":", ["serviceAccount", google_service_account.terraform.email]),
]
depends_on = [
google_service_account.terraform,
]
}
# Terraform へホストプロジェクトの編集ロールを付与
resource "google_project_iam_binding" "editor" {
project = google_project.terraform.project_id
for_each = toset([
"roles/editor",
])
role = each.value
members = [
join(":", ["group", var.admin_user_group.email]),
]
depends_on = [
google_service_account.terraform,
]
}
# Terraform へ組織内のプロジェクト作成権限を付与
resource "google_organization_iam_binding" "terraform" {
org_id = google_project.terraform.org_id
for_each = toset([
"roles/resourcemanager.projectCreator",
# "roles/billing.projectManager",
"roles/billing.user",
"roles/compute.xpnAdmin",
"roles/resourcemanager.projectIamAdmin",
"roles/resourcemanager.organizationAdmin",
"roles/orgpolicy.policyAdmin",
"roles/resourcemanager.folderAdmin",
"roles/accesscontextmanager.policyAdmin", # VPC SC 時に必要
])
role = each.value
members = [
join(":", ["serviceAccount", google_service_account.terraform.email]),
# join(":", ["user", var.admin_user.email]),
join(":", ["group", var.admin_user_group.email])
]
depends_on = [
google_project.terraform,
google_service_account.terraform,
]
}
# Terraform へ課金アカウントの利用権限を付与
resource "google_billing_account_iam_binding" "user" {
billing_account_id = google_project.terraform.billing_account
role = "roles/billing.user"
members = [
join(":", ["serviceAccount", google_service_account.terraform.email]),
# join(":", ["user", var.admin_user.email]),
join(":", ["group", var.admin_user_group.email])
]
depends_on = [
google_project.terraform,
]
}
# Terraform のステートファイル置き場の作成
resource "google_storage_bucket" "terraform" {
name = join("-", [google_project.terraform.project_id, "terraform-backet"])
project = google_project.terraform.project_id
location = "US"
force_destroy = true
storage_class = "STANDARD"
lifecycle_rule {
condition {
num_newer_versions = 5
}
action {
type = "Delete"
}
}
depends_on = [
google_project.terraform,
]
}
下記を出力するようにしている
- 作成したterraform サービスアカウント名
- backend.tf の作成コマンド
- terraform 用のサービスアカウントのキーファイル作成コマンド、移動コマンド
output "A001_Terraform_Service_Account" {
value = google_service_account.terraform.email
description = "Terraform Account"
}
output "A002_GCP_BACKEND" {
value = join("\n", [
"cat > backend.tf << EOF",
"terraform {",
" backend \"gcs\" {",
" bucket = \"${google_storage_bucket.terraform.name}\"",
" prefix = \"terraform/state\"",
" }",
"}",
"EOF",
"mv backend.tf ../",
])
}
output "A003_Next_Commands" {
value = join("", ["gcloud iam service-accounts keys create terraform_serviceacoount_credential.json --iam-account ", google_service_account.terraform.email, ";cp terraform_serviceacoount_credential.json ../;cd ../"])
description = "Next"
}
実行
下記コマンドで prestage 内の terraform を実行する
cd prestage
terraform workspace new dev
terraform apply
上記実行後Outputs:
で出力される情報を参考に、backend.tf
の作成と、terraformのクレデンシャルjsonファイル
の作成を実施する
以上で、terraform を実行する前の事前準備が完了 (参照先のterraterm実行までの手動設定箇所)
3. 組織の設定 (organaization.tf)
ここでは、組織全般の設定やユーザグループへの権限付与、1階層目のフォルダを作成する
以前の組織・共有 VPC 手動作成では下記の管理ユーザグループ・組織ポリシー・フォルダ管理を実施してなかったので、ここに内容を記載する
管理ユーザグループ
管理するグループはエンタープライズ向けガイドを参照している
個人的に作成してて試せればいいので今回は抜粋して2つのグループで設定する(サービスプロジェクトは別でグループを作成する)
CloudIdentity の設定は、今回 Terraform の対象にしていないので、事前にグループを作成しておく必要がある
グループ | 内容 |
---|---|
gcp-organization-admins | 組織の設定を管理する |
gcp-network-admins | ネットワークを管理する。共有 VPC など管理する |
terraform サービスアカウントも同じ操作が必要なため、同じ権限を与える
また、ドメイン全体に組織とフォルダの閲覧権限を与えている (フォルダ構成などはドメインユーザはみんな見えるようにしている)
組織ポリシー
以前作成時は設定しなかったが、サービスプロジェクトを作成するときなどに、共有 VPC 以外のネットワークをデフォルトで作成させないように、
プロジェクト作成時にデフォルトでネットワークを作らないようなポリシーを設定する
compute.skipDefaultNetworkCreation
を有効化する (下記は Web コンソールで見た対象ポリシー)
フォルダ
ホストプロジェクトを担当する部署をinfrastructure
、
サービスプロジェクトを担当する部署をservice
という名前でフォルダを作成する
orgnaization.tf
変数は下記を利用する
※今後追加してもまとめられやすいようにtype を obuject にしているが行数増えてしまった…
xxx
でマスクしているところは、**2. terraform 管理プロジェクトの作成 (prestage/)同様に実施環境にあった内容を埋める
terraform-service-accounts
は2. terraform 管理プロジェクトの作成 (prestage/)**で最後にOutput:
で出力されたアカウントを入れる
region / zone はリソースを作成する際にデフォルトで使用するものを記載する (ここでは無料枠が適用されやすいようにus-west1
にしている)
variable "gcp_common" {
type = object({
org_name = string
org_id = string
billing_account = string
region = string
zone = string
})
default = {
org_name = "xxxxxx"
org_id = "xxxxxxxxxxxx"
billing_account = "xxxxxx-xxxxxx-xxxxxx"
region = "us-west1"
zone = "us-west1-b"
}
validation {
condition = (length(regexall(var.gcp_common.region, var.gcp_common.zone)) > 0)
error_message = "Zone must be in region."
}
}
variable "terraform-service-accounts" {
type = string
default = "terraform@[terraform管理プロジェクト名].iam.gserviceaccount.com"
}
variable "organization_admin_group" {
type = object({
email = string
})
default = {
email = "gcp-organization-admin@[xxx.xxx:ドメイン名]"
}
}
variable "network_admin_group" {
type = object({
email = string
})
default = {
email = "gcp-network-admin@[xxx.xxx:ドメイン名]"
}
}
variable "domain" {
type = string
default = "[xxx.xxx:ドメイン名]"
}
メインコードは下記の通り
## 組織ポリシー
### デフォルトネットワーク作成の無効化
resource "google_organization_policy" "skipDefaultNetworkCreation" {
org_id = var.gcp_common.org_id
constraint = "compute.skipDefaultNetworkCreation"
boolean_policy {
enforced = true
}
}
# ドメインユーザに組織・フォルダ構成の閲覧権限付与
resource "google_organization_iam_binding" "organization_domain_viewer" {
org_id = var.gcp_common.org_id
for_each = toset([
"roles/resourcemanager.organizationViewer",
"roles/resourcemanager.folderViewer",
])
role = each.value
members = [
join(":", ["domain", var.domain]),
]
}
# 組織管理者への管理権限付与
resource "google_organization_iam_binding" "organization_org_admin" {
org_id = var.gcp_common.org_id
for_each = toset([
"roles/resourcemanager.organizationAdmin",
"roles/billing.admin",
"roles/resourcemanager.folderAdmin",
"roles/resourcemanager.projectCreator",
"roles/iam.organizationRoleAdmin",
"roles/orgpolicy.policyAdmin", # 組織ポリシー管理者
"roles/accesscontextmanager.policyAdmin", # VPC SC 時に必要
])
role = each.value
members = [
join(":", ["group", var.organization_admin_group.email]),
join(":", ["serviceAccount", var.terraform-service-accounts]),
]
# 削除すると管理者が削除されてしまうので偶発的な破壊を防ぐ
# 全体を削除する場合は、管理系を手動で逃してあげる必要がある
lifecycle {
prevent_destroy = true
# ignore_changes = all
}
}
# ネットワーク管理者への共有VPC等の権限付与
resource "google_organization_iam_binding" "organization_network_admin" {
org_id = var.gcp_common.org_id
for_each = toset([
"roles/compute.networkAdmin",
"roles/compute.xpnAdmin",
"roles/compute.securityAdmin",
])
role = each.value
members = [
join(":", ["group", var.network_admin_group.email]),
join(":", ["serviceAccount", var.terraform-service-accounts]),
]
}
# インフラ向けフォルダ
resource "google_folder" "organization_infrastructure_folder" {
display_name = "infrastructure"
parent = join("/", ["organizations", var.gcp_common.org_id])
depends_on = [
google_organization_policy.skipDefaultNetworkCreation,
]
}
# サービス向けフォルダ
resource "google_folder" "organization_service_folder" {
display_name = "service"
parent = join("/", ["organizations", var.gcp_common.org_id])
depends_on = [
google_organization_policy.skipDefaultNetworkCreation,
]
}
以降、すべてコードを記載していると長くなってきたので、特記箇所のみ記載して、特記以外は GitHub 参照とする (各章にコード参照リンクを載せている)
4. ホストプロジェクトの作成 (host.tf)
ホストプロジェクの作成や API, IAM 設定を実施している
variables.tf
の host_project_admin_group
のemail
にホストプロジェクトの管理グループのアドレスを入れる
共有 VPC のホスト有効化にはgoogle_compute_shared_vpc_host_project
を使用する
resource "google_compute_shared_vpc_host_project" "host" {
project = google_project.host_project.name
depends_on = [
google_project.host_project,
google_project_service.host_api_enable,
]
}
5. サービスプロジェクトの作成 (service1.tf)
サービスプロジェクトの作成や API, IAM 設定を実施している
variables.tf
の service1_project_admin_group
のemail
にサービスプロジェクトの管理グループのアドレスを入れる
サービスプロジェクトはgoogle_compute_shared_vpc_service_project
でhost_projectを指定して接続する
また、GCE などのためにサービスアカウントも2つ、Public 用と、Private 用に作成した
これは、Firewall で区別をできるようにするためにしている
# サービスプロジェクト設定
resource "google_compute_shared_vpc_service_project" "service1" {
host_project = google_project.host_project.name
service_project = google_project.service1.name
depends_on = [
google_project_service.service1_api_enable,
google_compute_shared_vpc_host_project.host,
]
}
# サービスアカウント
## Public 向け
resource "google_service_account" "service1_public_account" {
account_id = "service1-public-account-id"
display_name = "Service1 Public Account"
project = google_project.service1.name
}
## Private 向け
resource "google_service_account" "service1_private_account" {
account_id = "service1-private-account-id"
display_name = "Service1 Private Account"
project = google_project.service1.name
}
6. サブネットの作成 (subnet.tf)
サブネットの作成をgoogle_compute_subnetwork
で実施する
アドレスは172.18.0.0/24
を例として設定している (運用によって variables.tf
や tfvars
にするのもいいと思われる)
サービスプロジェクトで共有 VPC のサブネットを利用できるように、
google_compute_subnetwork_iam_binding
でサービスプロジェクトユーザグループにネットワークユーザ権限を付与している
※ホストプロジェクトでサブネット全体に付与も可能だが、ここではサブネットごとの権限管理にしている
resource "google_compute_subnetwork_iam_binding" "service1-gce-subnets" {
project = google_compute_subnetwork.service1-gce-subnets.project
region = google_compute_subnetwork.service1-gce-subnets.region
subnetwork = google_compute_subnetwork.service1-gce-subnets.name
role = "roles/compute.networkUser"
members = [
join(":", ["group", var.service1_project_admin_group.email]),
]
}
7. ファイアウォールの設定 (firewall.tf)
ファイアウォール設定を実施する
ファイアウォールルールとしては下記2つを今回のコードでは記載している
name | 内容 | 送信元 | 送信先 |
---|---|---|---|
private-private-001 | Privateアドレス内のアクセス許可.ゆるい設定なので環境によって要修正 | 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 | サービス1プロジェクト向け Private/Public サービスアカウント |
internet-public-001 | インターネットから INGRESS で許可する IP 設定 ここでは例として、外部テザリング環境などでログインする際にソフトバンク携帯のアドレスとして公開されている[サイト] (https://www.support.softbankmobile.co.jp/partner/home_tech1/index.cfm) iphone/ipadはこちらでした(追記) から抜粋したアドレスを許可している |
許可するグローバルアドレスリスト(参照) iphone/ipadはこちらでした(追記) | サービス1プロジェクト向け Public サービスアカウント |
resource "google_compute_firewall" "private-permit" {
name = "private-private-001"
description = "Private Subnet Permit"
network = google_compute_network.host_sharedvpc.name
priority = 1000
direction = "INGRESS"
project = google_project.host_project.name
source_ranges = ["192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8"]
target_service_accounts = [
google_service_account.service1_public_account.email,
google_service_account.service1_private_account.email,
]
allow {
protocol = "icmp"
}
allow {
protocol = "tcp"
ports = ["22"]
}
depends_on = [
google_compute_network.host_sharedvpc,
google_project_service.host_api_enable,
google_service_account.service1_public_account,
google_service_account.service1_private_account,
]
}
resource "google_compute_firewall" "softbank-mobile" {
name = "internet-public-001"
description = "Softbank Moible Global Address Permit"
network = google_compute_network.host_sharedvpc.name
priority = 1000
direction = "INGRESS"
project = google_project.host_project.name
# https://www.support.softbankmobile.co.jp/partner/home_tech1/index.cfm
# 2021.03.20 時点
source_ranges = [
"123.108.237.128/28",
#...(省略)...
]
target_service_accounts = [
google_service_account.service1_public_account.email,
]
allow {
protocol = "tcp"
ports = ["22"]
}
depends_on = [
google_compute_network.host_sharedvpc,
google_project_service.host_api_enable,
google_service_account.service1_public_account,
google_service_account.service1_private_account,
]
}
8. DNS の設定(限定公開 Google アクセス) (dns.tf)
限定公開 Google アクセス用に DNS を設定する
設定内容は基本的にはドキュメント通り
追加として(ドキュメントにも補足説明がある)、gcr.ioへの設定も合わせて実施している
また、cloudbilling
APIは restricted でのアクセスに対応していないため、private でのアクセスを利用できるように設定をしている
# cloudbilling へのアクセスがrestricted に未対応のため
resource "google_dns_record_set" "googleapis_cname2" {
name = "cloudbilling.googleapis.com."
managed_zone = google_dns_managed_zone.googleapis.name
type = "CNAME"
ttl = 300
rrdatas = ["private.googleapis.com."]
project = google_project.host_project.name
depends_on = [
google_dns_managed_zone.googleapis,
google_dns_record_set.googleapis_cname,
]
}
9. HA VPN の作成 (vpn.tf)
下記で記載した内容とほぼ同じ構成・パラメータで作成している (詳細は下記参照)
Output:
で上記qiitaで書いたような EdgeRouter Config 例を、HA VPN のグローバルIPなどを反映した形で出力するようにしている
変数は下記を利用するので、環境に合わせて更新する
variable "vpn" {
type = object({
peer_global_ip_address = string
peer_private_ip_address = string
shared_secret = string
peer_asn = number
asn = number
})
default = {
peer_global_ip_address = "自宅ラボ側グローバルIP"
peer_private_ip_address = "自宅ラボ側ルータ プライベートIP"
shared_secret = "シークレット(右コメントで生成)" # openssl rand -base64 24
peer_asn = 65001
asn = 65101
}
}
10. インターネット向け NAT の設定 (cloudnat.tf)
プライベートIPのみのインスタンスがインターネットと通信可能な用に、CloudNATを設定している
11. VPC Service Controls の作成 (vpc_service_controls.tf)
VPC Service Controls について下記にまとめている
今回は試しとして、storage.googleapis.com
をホスト・サーピスプロジェクトを境界線の内側に保護するようにしている
resource "google_access_context_manager_access_policy" "access-policy" {
parent = "organizations/${var.gcp_common.org_id}"
title = "my policy"
depends_on = [
google_organization_iam_binding.organization_org_admin,
]
}
resource "google_access_context_manager_service_perimeters" "service-perimeter" {
parent = "accessPolicies/${google_access_context_manager_access_policy.access-policy.name}"
service_perimeters {
name = "accessPolicies/${google_access_context_manager_access_policy.access-policy.name}/servicePerimeters/Perimeter1"
title = "Service Production Perimeter1"
perimeter_type = "PERIMETER_TYPE_REGULAR"
status {
restricted_services = ["storage.googleapis.com"]
resources = [
"projects/${google_project.host_project.number}",
"projects/${google_project.service1.number}",
]
}
}
}
以上で今回準備したコードの説明は終わり
実行は下記コマンドで実施する(環境をdev
とする場合)
terraform workspace new dev
terraform apply
おわりに
いままで手動で実施していた GCP 構築を Terraform で IaC 化することができた
※途中でまとめる時間が足りなくなって雑な記載になっているので、後ほど清書したい…
Terraform はドキュメントが整理されて例もわかりやすいので、基本はドキュメントを読めば大体使い方がわかった
(ドキュメント(provider google 箇所):https://registry.terraform.io/providers/hashicorp/google/latest/docs)
Terraform 初心者のため、良い記載方法や良いディレクトリ構成がわかれば修正をしていきたい
今後は、Google Cloud 向けの module が公開されているのでそちらの利用も検討したい
(Terraform modules for Google Cloud: https://github.com/terraform-google-modules)
(Terroform サイトにも説明あり: https://registry.terraform.io/providers/hashicorp/google/latest)
下記実施してて個人的に詰まったポイントを共有する
実施して困った Tips
IAMの状態反映
組織の IAM はデフォルトで、ドメインに請求先アカウント作成者
とプロジェクト作成者
が設定されている(下記デフォルト状態例)
Terraform での Role 設定は、設定する Role の権限を持っているユーザを記載状態に維持するように設定するため、
記載していない既存のユーザ設定がある場合、消してしまう
よって、例えば組織の管理者
を terraform で設定してから、terraform destroy
をすると設定がデフォルトに戻るわけではなくロール所属ユーザ設定が消えるので注意が必要
既存のロールをいじる際は要注意だが、新規なものもデフォルトで作成されるものを、2回目には記載のもののみに修正するのでやはり注意が必要
プロジェクト API
プロジェクト作成google_project
と API の設定google_project_service
を別の Terraform で実施しようとしたところ、
google_project
のみだとプロジェクトの API がゼロの状態でプロジェクトが構築される状態のため、google_project_service
を使用するために必要な API を terraform で有効化できない状態になった
google_project
と google_project_service
は同じ terraform で実施する必要がある
参考
Creating Google Cloud projects with Terraform
https://cloud.google.com/community/tutorials/managing-gcp-projects-with-terraform
Identity and Access Management > ドキュメント > 概要 > リソース階層
https://cloud.google.com/iam/docs/overview?hl=ja#resource-hierarchy
Best practices for enterprise organizations
https://cloud.google.com/docs/enterprise/best-practices-for-enterprise-organizations