【NIC x Ops(にこぷす)通信】(Terraform編)【初心者向け】TerraformでAWSとGoogle Cloudのマルチクラウド環境を爆速で同時構築してみた!
投稿者:清水
目次
- はじめに
- 今回作成するマルチクラウド全体構成図
- ディレクトリ構成とソースコードの役割
- 【超重要】安全な認証設定 (AWS OIDC / GCP Workload Identity)
- 設定しているVariables(変数)一覧
- GitHub × HCP Terraformによる自動連動(GitOps)
- まとめ:IaCでマルチクラウドを統治しよう!
皆さま、こんにちは!
このテックブログでは、IT運用に関する内容を連載、「にこぷす通信」をお届けしています。
「NIC x Ops(にこぷす)」とは?
『NIC x Ops(にこぷす)』とは、
私たちの会社であるNTTインテグレーション(NI+C)の運用ノウハウと、
IBMさんの強力なソリューションを
Collaboration(コラボレーション)させた、IT運用高度化シリーズ(Ops)のニックネームなんです。
現代のIT運用の現場は、システムの複雑化や人手不足など、課題が山積みです。そんな現場を少しでも楽に、そしてエンジニアの皆さんが笑顔で「攻めの運用」ができるように支えたい。そんな想いがこの「にこぷす」には込められています。
「にこぷす」には、Instana(可観測性)のほかにも、Turbonomic(リソース最適化)やTerraform(インフラ自動化)など、IT運用を劇的に変える製品がたくさんあります。
これからもこの通信を通して、「IT用語は難しいけれど、中身を知ればこんなに便利で面白いんだ!」というテクニカルな内容をご紹介していきますので、どうぞよろしくお願いします。
1.はじめに
今回は、インフラのコード化(IaC)ツールとして大人気の Terraform を使って、AWS(Amazon Web Services) と Google Cloud(GCP) の2大クラウドに同時にインフラ環境を構築する「マルチクラウド構築」に挑戦してみました!
「マルチクラウドって難しそう…」と思うかもしれませんが、Terraformを使えば1つの設定ファイルから両方のクラウドを同時にコントロールできます。
さらに今回は、GitHubとHCP Terraformを自動連動させ、コードをプッシュするだけで自動的にterraform planが動くGitOpsな環境も合わせて構築しました。初心者の方にも分かりやすいよう、ソースコードの解説から、安全な接続に不可欠な認証設定(IAM / Workload Identity)の手順まで丁寧に解説します!
2.今回作成するマルチクラウド全体構成図
今回は、AWSとGoogle CloudのそれぞれにWebサーバーとデータベース、そしてそれらを収めるネットワーク(VPC)を同時に作成します。作成するリソースの合計は以下の通りです。
3.ディレクトリ構成とソースコードの役割
今回のプロジェクトのファイル構成は非常にシンプルです。役割ごとに3つのファイルに分割しています。
# ================================================================
# 1. AWS リソース定義
# ================================================================
# ネットワーク (VPC & Subnets)
resource "aws_vpc" "main_vpc" {
cidr_block = var.aws_vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "example-vpc"
}
}
resource "aws_subnet" "public_1" {
vpc_id = aws_vpc.main_vpc.id
cidr_block = var.aws_public_subnet_1_cidr
availability_zone = "${var.aws_region}a"
tags = {
Name = "example-public-subnet-1"
}
}
resource "aws_subnet" "public_2" {
vpc_id = aws_vpc.main_vpc.id
cidr_block = var.aws_public_subnet_2_cidr
availability_zone = "${var.aws_region}c"
tags = {
Name = "example-public-subnet-2"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main_vpc.id
tags = {
Name = "example-igw"
}
}
resource "aws_route_table" "public_rt" {
vpc_id = aws_vpc.main_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
tags = {
Name = "example-public-rt"
}
}
resource "aws_route_table_association" "a" {
subnet_id = aws_subnet.public_1.id
route_table_id = aws_route_table.public_rt.id
}
resource "aws_route_table_association" "b" {
subnet_id = aws_subnet.public_2.id
route_table_id = aws_route_table.public_rt.id
}
# セキュリティグループ
resource "aws_security_group" "web_sg" {
name = "example-web-sg"
description = "Allow HTTP"
vpc_id = aws_vpc.main_vpc.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "db_sg" {
name = "example-db-sg"
vpc_id = aws_vpc.main_vpc.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.web_sg.id]
}
}
# Amazon EC2 インスタンス (Web App)
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical社の公式アカウントID
}
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public_1.id
vpc_security_group_ids = [aws_security_group.web_sg.id]
associate_public_ip_address = true
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y apache2
echo "Welcome to Example Web App (AWS Server \${count.index + 1})" > /var/www/html/index.html
systemctl enable apache2
systemctl start apache2
EOF
tags = {
Name = "example-web-server-\${count.index + 1}"
}
}
# Amazon RDS (MySQL)
resource "aws_db_subnet_group" "db_subnets" {
name = "example-db-subnet-group"
subnet_ids = [aws_subnet.public_1.id, aws_subnet.public_2.id]
}
resource "aws_db_instance" "mysql" {
allocated_storage = 20
db_name = "exampledb"
engine = "mysql"
engine_version = "8.0"
instance_class = "db.m7g.xlarge"
username = var.aws_db_username
password = var.aws_db_password
db_subnet_group_name = aws_db_subnet_group.db_subnets.name
vpc_security_group_ids = [aws_security_group.db_sg.id]
skip_final_snapshot = true
publicly_accessible = false
}
# ================================================================
# 2. Google Cloud (GCP) リソース定義
# ================================================================
# VPCネットワーク
resource "google_compute_network" "gcp_vpc" {
name = "example-gcp-vpc"
auto_create_subnetworks = false
}
# サブネット
resource "google_compute_subnetwork" "gcp_subnet" {
name = "example-gcp-subnet"
ip_cidr_range = var.gcp_subnet_cidr
region = var.gcp_region
network = google_compute_network.gcp_vpc.id
}
# ファイアウォール規則 (HTTP 80番ポートを許可)
resource "google_compute_firewall" "allow_http" {
name = "example-gcp-allow-http"
network = google_compute_network.gcp_vpc.name
allow {
protocol = "tcp"
ports = ["80"]
}
source_ranges = ["0.0.0.0/0"]
target_tags = ["web-server"]
}
# Compute Engine (仮想マシン)
resource "google_compute_instance" "gcp_web" {
count = var.instance_count
name = "example-gcp-web-\${count.index + 1}"
machine_type = "e2-micro"
zone = var.gcp_zone
tags = ["web-server"]
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2204-lts"
}
}
network_interface {
subnetwork = google_compute_subnetwork.gcp_subnet.id
access_config {
// 外部IPアドレス(パブリックIP)を自動付与
}
}
# 起動スクリプト(Apacheのインストール)
metadata_startup_script = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y apache2
echo "Welcome to Example Web App (GCP Server \${count.index + 1})" > /var/www/html/index.html
systemctl enable apache2
systemctl start apache2
EOF
}
# Cloud SQL (MySQL データベース)
resource "google_sql_database_instance" "gcp_mysql" {
name = "example-gcp-db"
database_version = "MYSQL_8_0"
region = var.gcp_region
settings {
tier = "db-custom-16-65536"
}
deletion_protection = false # デモ終了後にdestroyしやすくする設定
}
# ----------------------------------------------------------------
# 共通変数
# ----------------------------------------------------------------
variable "instance_count" {
type = number
default = 2
description = "AWSとGCPのそれぞれに作成するデモ用Webサーバー数"
}
# ----------------------------------------------------------------
# AWS用変数
# ----------------------------------------------------------------
variable "aws_region" {
type = string
default = "ap-northeast-1"
description = "AWSリソースをデプロイするリージョン"
}
variable "aws_vpc_cidr" {
type = string
default = "10.0.0.0/16"
description = "AWS VPCのCIDRブロック"
}
variable "aws_public_subnet_1_cidr" {
type = string
default = "10.0.1.0/24"
description = "AWS パブリックサブネット1のCIDR"
}
variable "aws_public_subnet_2_cidr" {
type = string
default = "10.0.2.0/24"
description = "AWS パブリックサブネット2のCIDR"
}
variable "aws_db_username" {
type = string
default = "admin"
description = "AWS RDSのマスターユーザー名"
}
variable "aws_db_password" {
type = string
default = "your-rds-password-here" # 【要変更】安全のため、実際のパスワードはここには書かず環境変数やtfvarsファイルで管理してください
sensitive = true
description = "AWS RDSのマスターパスワード"
}
# ----------------------------------------------------------------
# Google Cloud (GCP) 用変数
# ---------------------------------------------------------------
variable "gcp_project_id" {
type = string
default = "your-gcp-project-id" # 【要変更】あなたのGCPプロジェクトID(英数字のID)に置き換えてください
description = "Google CloudのプロジェクトID"
}
variable "gcp_region" {
type = string
default = "asia-northeast1" # 東京リージョン
description = "Google Cloudのデプロイ先リージョン"
}
variable "gcp_zone" {
type = string
default = "asia-northeast1-a"
description = "Google Cloudのデプロイ先ゾーン"
}
variable "gcp_vpc_cidr" {
type = string
default = "172.16.0.0/16"
description = "GCP VPCネットワークのCIDR"
}
variable "gcp_subnet_cidr" {
type = string
default = "172.16.1.0/24"
description = "GCP サブネットのCIDR"
}
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
# HCP Terraform (クラウドState管理) の設定
cloud {
organization = "your-organization-name" # 【要変更】あなたのHCP Terraform of Organization名
workspaces {
name = "your-workspace-name" # 【要変更】あなたのHCP Terraform of Workspace名
}
}
}
# AWSプロバイダの設定
provider "aws" {
region = var.aws_region
}
# Google Cloudプロバイダの設定
provider "google" {
project = var.gcp_project_id
region = var.gcp_region
zone = var.gcp_zone
}
セキュリティを高めるためのTips
本番運用では、機密情報をコード(variables.tf の default や terraform.tfvars)に直書きしてバージョン管理(Gitなど)にコミットするのはNGです。
ローカル環境で検証する場合は terraform.tfvars に記載した上で必ず .gitignore で除外してください。また、本記事のように本番運用で HCP Terraform を利用して自動化を行う場合は、default 値を設定せず空(未定義)にした上で、HCP Terraformの「Variables」機能(Sensitive属性)を使って安全に注入する手法が安心でスマートな方法です!設定漏れによるトラブルも防げてスマートに管理できます。
4.【超重要】安全な認証設定 (AWS OIDC / GCP Workload Identity)
HCP Terraformが各クラウドを操作するための認証設定を行います。漏洩リスクのある固定キーではなく、一時的なアクセス権を安全に貸し出す OIDC(OpenID Connect) を採用します。
🛠️ AWS側での OIDC (IDプロバイダ & IAMロール) 設定手順
AWSに「アクセスキー」を作らせず、HCP Terraformからのアクセスに対して、その都度「一時的なアクセス権(トークン)」を発行するための仕組みを設定します。
全体の流れは以下の3ステップです。
- AWS側で OIDC プロバイダを作成する
- HCP Terraform専用の IAM ロールを作成する
- 信頼関係(ポリシー)を調整する(※最重要)
【ステップ1】AWS側で OIDC プロバイダを作成する
まずは、HCP Terraformというサービス全体を、AWSが信頼する接続相手として登録します。
下記のタイムラインに沿って設定を進めてください。
IAM画面へアクセス
AWSコンソールにログインし、上部の検索窓から IAM を検索して開きます。
IDプロバイダーを開く
左側のメニューから 「アクセス管理」➔「IDプロバイダー」 をクリックします。
プロバイダーの追加
右上の 「プロバイダーを追加」 ボタンをクリックします。
設定値の入力
プロバイダータイプで「OpenID Connect」を選択し、以下を指定します:
-
プロバイダーのURL:
[https://app.terraform.io](https://app.terraform.io)※末尾に / を入れない -
対象者 (Audience):
aws.workload.identity
プロバイダーを追加して完了
入力に誤りがないことを確認し、右下の 「プロバイダーを追加」 ボタンをクリックして完了です。
【ステップ2】HCP Terraform専用の IAM ロールを作成する
次に、OIDC接続してきたHCP Terraformに対して、どのような操作を許可するかを定義した「お面(ロール)」を作ります。
ロール作成画面を開く
IAM画面の左メニューから 「ロール」 をクリックし、右上の 「ロールを作成」 をクリックします。
信頼されたエンティティの選択
信頼されたエンティティタイプで 「ウェブアイデンティティ」 を選択します。
IDプロバイダーの選択
「IDプロバイダー」のプルダウンから、先ほど作成した `app.terraform.io` を選択します。
Audience(対象者)の選択
プルダウンから `aws.workload.identity` を選択します。
Workload条件の指定
画面が切り替わり、詳細設定が表示されるので以下を指定します:
- Workload Type:
Workspace Runにチェックを入れます。 - organization: あなたのHCP Terraform組織名(例:
your-organization-name)を記入します。 -
WorkSpace:
*(半角アスタリスク)を記載します。 💡今回の構成では、2つのWorkspace(AWS用・GCP用)から接続したいため、ワイルドカード(*)で全体を許容します。 - Run Phase:
*(半角アスタリスク)を記載します。
次へ進む
入力が終わったら、右下の 「次へ」 をクリックします。
許可ポリシーの選択
今回はVPC、EC2、RDSなど広範囲のリソースを検証で自動構築するため、一時的に `AdministratorAccess` ポリシーにチェックを入れて、「次へ」 をクリックします。 ⚠️注意:本番運用では最小権限の法則に従い、必要な権限のみをアタッチしてください。
ロール名の指定
ロール名に `example-hcp-terraform-role` と入力し、最下部までスクロールします。
ロールの作成
右下の 「ロールを作成」 をクリックして保存します。
【ステップ3】信頼関係(ポリシー)の調整(※超重要)
ここが一番大切です!
このままの設定では、「世界中のHCP Terraformユーザー(他人の組織)」が、あなたのAWSアカウントにログインできてしまう状態になっています。 そこで、「あなたのHCP Terraform組織の、このプロジェクトから接続されたときだけ」 ログインを許可するように、厳格な制限をかけます。
ロールの詳細を開く
先ほど作成したロール `example-hcp-terraform-role` の詳細画面を開きます。
信頼関係の編集
「信頼関係」 タブをクリックし、「信頼ポリシーを編集」 をクリックします。
ポリシーJSONの書き換え
エディター内の JSON を、以下の内容に丸ごと書き換えます:
⚠️ 実行前に、<あなたのAWSアカウントID> および <あなたのHCP組織名> の箇所を必ずご自身の環境のものに書き換えてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<あなたのAWSアカウントID>:oidc-provider/app.terraform.io"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"app.terraform.io:aud": "aws.workload.identity"
},
"StringLike": {
"app.terraform.io:sub": "organization:<あなたのHCP組織名>:project::workspace::run_phase:*"
}
}
}
]
}
信頼ポリシーを保存
右下の 「ポリシーを更新」 をクリックして保存します。
これで、HCP TerraformとAWSの間の安全な認証ルート(OIDC)が完成しました!
次はGoogle Cloud側の認証設定へ進みましょう。
☁️ Google Cloud (GCP) 側での OIDC 認証設定手順について
AWS側の設定に続き、マルチクラウドのもう片方である Google Cloud (GCP) に対しても、同様に固定のサービスアカウントキーを排し、安全な Workload Identity(OIDC認証) を設定していく必要があります。
Google Cloud側でのWorkload Identityの設定手順、およびVPC-SC(VPC Service Controls)などのより高度でセキュアな環境下での管理手順については、弊ブログの 【NIC x Ops(にこぷす)通信】 シリーズにて、設定画面のキャプチャを交えて詳しく丁寧に解説しています。
GCP設定の詳細なステップ・バイ・ステップ手順については、ぜひ以下の連携元記事をご参照ください!
5.設定しているVariables(変数)一覧
HCP Terraformの該当するWorkspaceを開き、「Variables」 メニューをクリックして、以下の 「Environment variable」を追加します。

| 変数名(Key) | 値の記述形式(Value) | 用途 |
|---|---|---|
| AWS_DEFAULT_REGION | ap-northeast-1 | プロビジョニング対象となるAWSのデフォルトリージョン。 |
| TFC_AWS_PROVIDER_AUTH | true | HCP Terraformに対して、AWSのキーレス認証(Dynamic Credentials)の稼働を命じるフラグ。 |
| TFC_AWS_RUN_ROLE_ARN | arn:aws:iam::【AWSアカウントID】:role/hcp-terraform-aws-demo-role | リソース構築用の権限として利用(AssumeRole)するAWS IAMロールのARN。 |
| TFC_GCP_PROJECT_NUMBER | 【プロジェクト番号】 | 対象となるGCPのプロジェクト番号。 |
| TFC_GCP_PROVIDER_AUTH | true | HCP Terraformに対して、GCP WIFのキーレス認証プロトコルの稼働を命じるフラグ。 |
| TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL | hcp-terraform-aws-gcp-demo-rol@【プロジェクトID】.iam.gserviceaccount.com | 最終的に境界をまたいで借用し、リソースの構築権限として利用するサービスアカウント。 |
| TFC_GCP_WORKLOAD_PROVIDER_NAME | projects/【プロジェクト番号】/locations/global/workloadIdentityPools/hcp-terraform-demo-pool/providers/hcp-terraform-demo-provider | GCPプロジェクトに構築したOIDCプロバイダのフルパス。 |
6.GitHub × HCP Terraformによる自動連動(GitOps)
コードの修正や各設定が完了したら、GitOpsによって自動的にクラウドへと反映させる仕組みを整えます。
開発者がコードをローカルで修正し、GitHubへプッシュします。
GitHubからWebhookで自動的にHCP Terraformに変更を通知します。
HCP Terraformがインフラ差分計画(plan)を実行し、画面に出力します。
💡 GitHub と HCP Terraform の自動連携がもたらすメリット
このワークフローを導入することで、これまで手動で行っていた「コードのチェック」や「インフラの差分確認(terraform plan)」の作業が100%自動化されます。
開発者は自分のPCから使い慣れたGitコマンドを実行するだけで、あとはシステムが裏側で全自動で処理を連携してくれます。
それぞれのステップで具体的に何が起きているのか、詳しく見ていきましょう。
1. git push(すべてのトリガー)
開発者がローカル環境(自分のPC)でTerraformの設定ファイルを修正し、GitHubの対象リポジトリへ git push します。 これがインフラ変更パイプライン全体の「スタートの合図」になります。
2. Webhook通知(システム間のリアルタイム連携)
コードのプッシュを検知したGitHubは、「Webhook(ウェブフック)」という仕組みを使って、HCP Terraformに対して「新しいコードが届いたよ!」という通知をリアルタイムに送信します。
補足:Webhookとは?
何かイベントが起きた瞬間に、別のシステムへ自動で業務連絡を送る仕組みのことです。数分おきに「更新はないかな?」と確認しにいく無駄な通信(ポーリング)とは違い、更新があった「その瞬間」だけ通信が発生するため、非常に高速かつ効率的です。
3. Auto Plan(全自動でのインフラ差分計算)
GitHubからの通知を受け取ったHCP Terraformは、自動的に新しいコードを読み込み、内部で terraform plan コマンドを実行します。 これにより、今回のコード変更によって「どのインフラリソースが追加・変更・削除されるのか」という計画(Plan)が画面上にすぐに出力されます。
HCP Terraform(Apply 結果)


Apply完了後、AWSとGCPでそれぞれリソースが作成されているのが確認できます。
実際に手動でAWSとGoogle Cloudのコンソールにログインし、ポチポチと画面をクリックしながら今回の構成(VPC、サブネット、VM×4台、DB×2台など)を構築しようとすると、慣れている人でも最低30分以上はかかってしまいます。設定ミスによる手戻りがあれば、さらに時間は伸びるでしょう。
しかし、このTerraformによる自動構築を使えば、コマンドを1回実行するだけで、わずか5分程度で完了してしまいます!
🚀 これぞ「GitOps」の第一歩!
手動で terraform plan を実行していた頃は、各自のPCの環境差異や、実行し忘れといった小さなミスが起こりがちでした。
コードをGitで管理し、それを引き金(トリガー)にしてクラウド上のTerraformを自動連動させるこの仕組みは、現代のインフラ管理手法である「GitOps(ギットオプス)」の王道パターンです。設定自体も非常にシンプルなので、ぜひ皆さんの環境でも試してみてください!
7.まとめ:IaCでマルチクラウドを統治しよう!
いかがでしたでしょうか?「マルチクラウドの構築」と聞くと、AWSとGoogle Cloudの両方の管理コンソールを行き来して大変なように思えます。
実際、手動で構築しようとすると画面の往復や設定ミスなども含めて30分以上はかかってしまう作業ですが、Terraformによる自動化(IaC)を使えば、わずか5分程度で安全に構築を完了させることができます!
Terraformを介すことで、全く同一の文法と流れで同時に安全なインフラを構築可能になります。
GitOps自動連携を構築したことで、今後はGitHubリポジトリに変更をプッシュするだけで、HCP Terraform上で即座に実行計画が可視化され、より迅速でバグの少ない開発が実現できます。
ぜひ、このソーステンプレートと安全なOIDC連携を参考に、ワンランク上のクラウド構築への一歩を踏み出してみましょう!
「にこぷす通信」について
「NIC × Ops(にこぷす)通信」は、隔週で皆さまに情報をお届けします。
このシリーズでは、Instana(可観測性)をはじめ、Turbonomic(リソース最適化)やTerraform(インフラ自動化)など、IT運用を劇的に変える製品をピックアップしていきます。
これからも最新の技術検証をチームで継続し、皆さまの現場ですぐに役立つ「有益な知見」を全力で共有していきます。 次回の連載もお楽しみに!