<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>運用 on 乱筆</title>
        <link>https://blog.l3zc.com/ja/tags/%E9%81%8B%E7%94%A8/</link>
        <description>Recent content in 運用 on 乱筆</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>ja</language>
        <lastBuildDate>Fri, 10 Apr 2026 00:28:53 +0800</lastBuildDate><atom:link href="https://blog.l3zc.com/ja/tags/%E9%81%8B%E7%94%A8/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Terraform IaC 初体験</title>
            <link>https://blog.l3zc.com/ja/2026/04/iac-with-terraform/</link>
            <pubDate>Thu, 09 Apr 2026 07:13:35 +0000</pubDate>
            <guid>https://blog.l3zc.com/ja/2026/04/iac-with-terraform/</guid>
            <description>&lt;img src=&#34;https://blog.l3zc.com/2026/04/iac-with-terraform/cover_hu_a8e83f97ed61569c.webp&#34; alt=&#34;Featured image of post Terraform IaC 初体験&#34; /&gt;&lt;p&gt;Terraform は IaC ツールであり、IaC とは「インフラストラクチャ・アズ・コード」を意味します。私たちが持つインフラストラクチャを宣言的なコードとして記述し、その後 &lt;code&gt;terraform apply&lt;/code&gt; を実行することでインフラストラクチャのデプロイが完了します。同一の設定であれば、必ず同じインフラストラクチャがデプロイされます（Nix OS ユーザーにとっては大喜びの内容です）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;terraform-を使用する理由&#34;&gt;Terraform を使用する理由&#xA;&lt;/h2&gt;&lt;p&gt;従来のインフラストラクチャ管理の大部分は人的作業と各種クラウドプロバイダーのダッシュボードに依存しており、以下のような課題が生じています：&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th style=&#34;text-align: left&#34;&gt;課題&lt;/th&gt;&#xA;          &lt;th style=&#34;text-align: left&#34;&gt;説明&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;再現性の欠如&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;ダッシュボードでのクリック操作による設定変更は、誤りや見落としが発生しやすく、復元も困難&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;環境ドリフト&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;手動操作により本番環境とテスト環境の設定が徐々に乖離し、極端なケースではテストでは問題ないのに本番でダウンすることがある&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;拡張の難しさ&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;新しい環境を追加するには多数の手動作業を繰り返す必要があり、時間がかかりミスも発生しやすい&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;監査の難しさ&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;変更履歴がないため、&lt;del&gt;トラブル発生時に責任の所在を特定するのが難しい&lt;/del&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;協力の難しさ&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td style=&#34;text-align: left&#34;&gt;インフラストラクチャは少数の「詳しい人」によって管理されており、誰かが変更したい場合でもその人を探さなければならず効率が悪い&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;これらの問題を解決するために、「インフラストラクチャ・アズ・コード」&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; という概念が提唱され、Terraform はその有名なソリューションの一つです。&lt;/p&gt;&#xA;&lt;p&gt;実際のユースケースを想定してみます。例えば、毎年新しい GCP の$300 無料クレジットアカウントを購入する場合、安価ですが毎年 GCP のパネルで再度マシンを立て直す必要があります。しかし、Terraform を使えば、同じ設定のマシンをデプロイしたい場合、アカウントを変更するたびに API Token を新しいものに置き換え、&lt;code&gt;terraform apply&lt;/code&gt; を実行するだけです。数分で前のアカウントと同じマシン、VPC、S3、ファイアウォール設定などを作成できます。&lt;/p&gt;&#xA;&lt;p&gt;また、Cloudflare DNS や Tunnel の管理・移行においても、元の Tunnel の Ingress Rule を新しい Tunnel の Ingress Rule にコピーするだけで済みます。将来的に AliDNS、Route 53 など他のプロバイダーに移行する場合でも、データをそのままコピーすれば&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;対応可能です。&lt;/p&gt;&#xA;&lt;p&gt;特に複数人で協力する場合、Git と組み合わせることで変更の痕跡が残り、マージ競合の心配もありません。PR には変更プレビューが自動生成され、問題が発生すればすぐに前バージョンにロールバックできます。これらは従来の人手によるプロバイダーダッシュボードへの直接操作では到底不可能なことです。&lt;/p&gt;&#xA;&lt;h2 id=&#34;terraform-のインストール&#34;&gt;Terraform のインストール&#xA;&lt;/h2&gt;&lt;p&gt;Terraform は Go で書かれており、コンパイルされた出力は単一の実行可能ファイルであるため、インストールも非常に簡単です。Windows では Winget を直接使用できます：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-pwsh&#34;&gt;winget install Hashicorp.Terraform&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Winget を使いたくない場合は、Scoop などの別のパッケージマネージャーを使用するか、事前コンパイルされたバイナリファイルを &lt;code&gt;$PATH&lt;/code&gt; に配置するだけでインストールが完了します。&lt;/p&gt;&#xA;&lt;p&gt;Linux 環境の場合は、対応するパッケージマネージャーを使用してインストールしてください。バイナリファイルを &lt;code&gt;$PATH&lt;/code&gt; に配置する方法を使う場合は、&lt;code&gt;sudo chmod +x terraform&lt;/code&gt; でファイルの実行権限を設定することを忘れないでください。&lt;/p&gt;&#xA;&lt;h2 id=&#34;terraform-の-2-つの基本概念&#34;&gt;Terraform の 2 つの基本概念&#xA;&lt;/h2&gt;&lt;h3 id=&#34;providers&#34;&gt;Provider(s)&#xA;&lt;/h3&gt;&lt;p&gt;前述の通り、Terraform はインフラストラクチャ・アズ・コードツールですが、ツール自体は特定のプラットフォームにバインドされておらず、Provider(s) を通じて各プラットフォームと連携します。利用可能な Provider(s) を確認するには、&lt;a class=&#34;link&#34; href=&#34;https://registry.terraform.io/browse/providers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Terraform のレジストリ&lt;/a&gt;を参照してください。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://blog.l3zc.com/2026/04/iac-with-terraform/image_hu_463ebc576c22c691.webp&#34; alt=&#34;Terraform は豊富な Provider(s) エコシステムを持つ&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&lt;h3 id=&#34;状態管理&#34;&gt;状態管理&#xA;&lt;/h3&gt;&lt;p&gt;Terraform はインフラストラクチャ変更操作を実行する際の状態情報を状態ファイルに保存します。デフォルトでは現在の作業ディレクトリ内の &lt;code&gt;terraform.tfstate&lt;/code&gt; ファイルに保存されます&lt;sup id=&#34;fnref:3&#34;&gt;&lt;a href=&#34;#fn:3&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;3&lt;/a&gt;&lt;/sup&gt;。構成ファイルを変更して他のストレージバックエンド（S3、Postgres など）を指定することも可能です。&lt;code&gt;terraform apply&lt;/code&gt; を実行するたびに、Terraform は現在の構成ファイルで宣言された状態と既存の状態ファイルを比較し、変更部分を計算して調整順序を自動的に決定した後、Provider を操作して状態変動を実現します。&lt;/p&gt;&#xA;&lt;h2 id=&#34;terraform-のリソースインポートの難題&#34;&gt;Terraform のリソースインポートの難題&#xA;&lt;/h2&gt;&lt;p&gt;既存リソースのインポートはこれまで Terraform が批判されてきた問題点であり、Hashi Corp. は一貫して「すべてのインフラストラクチャは最初から Terraform で作成されるべきであり、リソースインポートの問題などは存在しない」という頑固で愚かな見解を堅持しているようです。&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;時期&lt;/th&gt;&#xA;          &lt;th&gt;バージョン&lt;/th&gt;&#xA;          &lt;th&gt;進展&lt;/th&gt;&#xA;          &lt;th&gt;問題&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2014–2022&lt;/td&gt;&#xA;          &lt;td&gt;v0.x–v1.4&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;terraform import&lt;/code&gt; のみで、一度に 1 件ずつ、かつ全く設定を生成しない&lt;/td&gt;&#xA;          &lt;td&gt;インポート後でも自分で HCL&lt;sup id=&#34;fnref:4&#34;&gt;&lt;a href=&#34;#fn:4&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;4&lt;/a&gt;&lt;/sup&gt; を書く必要がある&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2023.06&lt;/td&gt;&#xA;          &lt;td&gt;v1.5&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;import&lt;/code&gt; ブロックと &lt;code&gt;-generate-config-out&lt;/code&gt; パラメータを導入し、設定を生成可能になった&lt;/td&gt;&#xA;          &lt;td&gt;それでも 1 行ずつ書く必要があり、既存リソースの ID も自分で提供しなければならない&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2024.01&lt;/td&gt;&#xA;          &lt;td&gt;v1.7&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;import&lt;/code&gt; ブロックが &lt;code&gt;for_each&lt;/code&gt; をサポート&lt;/td&gt;&#xA;          &lt;td&gt;バッチ処理が可能になったが、ID はまだ自分で用意する必要がある&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;2024 年下半期&lt;/td&gt;&#xA;          &lt;td&gt;v1.12&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;terraform query&lt;/code&gt; と &lt;code&gt;list&lt;/code&gt; ブロックを導入し、ついにリソースの自動発見機能を達成&lt;/td&gt;&#xA;          &lt;td&gt;ただしこの機能は Provider が実装する必要があり、多くの Provider が追いついていない&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;長年の間、コミュニティは批判し続けていますが、「既存のリソースをどうやって Terraform にインポートするか」という問題は真剣に取り組まれていませんでした。「インフラストラクチャ・アズ・コード」ツールである Terraform の設計哲学は宣言性と冪等性&lt;sup id=&#34;fnref:5&#34;&gt;&lt;a href=&#34;#fn:5&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;5&lt;/a&gt;&lt;/sup&gt;であり、Hashi Corp. は「すべてはコード声明された状態が基準であり、Terraform でゼロからリソースを作成すべきだ」と信じています。しかし現実には、ほとんどの企業には大量の歴史的残存リソースがあり、「先にリソースがあり、後にコードがある」のが常态です。この矛盾を Hashi Corp. が認めたのは遅く、&lt;code&gt;v1.12&lt;/code&gt; の &lt;code&gt;terraform query&lt;/code&gt; になって初めて公式にこの問題に対処しましたが、Provider エコシステムの追従状況……正直言って期待薄です。&lt;/p&gt;&#xA;&lt;h2 id=&#34;terraform-のファイル構造&#34;&gt;Terraform のファイル構造&#xA;&lt;/h2&gt;&lt;p&gt;Terraform のファイル構造はシンプルで、メインプログラムは実行時、作業ディレクトリ内のすべての &lt;code&gt;.tf&lt;/code&gt; ファイルを盲目的に読み取ります。情報さえ揃っていれば、ファイル名は何でも好きなように付けられます。私のファイル構造はこのようになります：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;❯ tree -a -I .git&#xA;.&#xA;├── .editorconfig&#xA;├── .github&#xA;│   ├── dependabot.yml&#xA;│   └── workflows&#xA;│       ├── terraform-apply.yml&#xA;│       ├── terraform-plan.yml&#xA;│       └── your-fork.yml&#xA;├── .gitignore&#xA;├── .terraform.lock.hcl&#xA;├── cf_dns_zones.tf&#xA;├── cf_tunnel.tf&#xA;├── dns_example_com.tf&#xA;├── dns_example_net.tf&#xA;├── dns_example_top.tf&#xA;├── dns_example_cn.tf&#xA;├── main.tf                 # ベース設定（terraform ブロック）&#xA;├── moved.tf                # 移動したリソース&#xA;├── provider.tf             # 各 Provider の設定&#xA;├── README.md&#xA;├── rename_resources.ps1&#xA;└── variables.tf            # カスタム変数&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;main.tf&lt;/code&gt;はベース設定を保存するために使用されます：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hcl&#34;&gt;terraform {&#xA;  required_providers {&#xA;    cloudflare = {&#xA;      source  = &#34;cloudflare/cloudflare&#34;&#xA;      version = &#34;~&gt; 5&#34;&#xA;    }&#xA;&#xA;    tencentcloud = {&#xA;      source  = &#34;tencentcloudstack/tencentcloud&#34;&#xA;      version = &#34;&gt;= 1.81.43&#34;&#xA;    }&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;provider.tf&lt;/code&gt;は各 Provider の設定を保存するために使用されます：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hcl&#34;&gt;provider &#34;cloudflare&#34; {}&#xA;provider &#34;tencentcloud&#34; {}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ここが空なのは、Credentials を構成ファイルに直接書くのではなく、環境変数を通じて渡せるからです。例えば Cloudflare の Provider は、&lt;code&gt;CLOUDFLARE_API_TOKEN&lt;/code&gt; を &lt;code&gt;api_token&lt;/code&gt; 変数の代替として受け入れます。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;variables.tf&lt;/code&gt;はカスタム変数を宣言するために使用されます：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;variable &#34;cloudflare_zone_example_com&#34; {&#xA;  description = &#34;example.com の Cloudflare zone ID&#34;&#xA;  type        = string&#xA;}&#xA;&#xA;variable &#34;cloudflare_zone_example_top&#34; {&#xA;  description = &#34;example.top の Cloudflare zone ID&#34;&#xA;  type        = string&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これらの変数は &lt;code&gt;TF_VAR_&lt;/code&gt; で始まる環境変数を通じて入力でき、Terraform は &lt;code&gt;terraform.tfvars&lt;/code&gt; ファイルの変数も自動的に読み取ります。変数の主な用途は、他の構成ファイルから呼び出すことです。例えば：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hcl&#34;&gt;resource &#34;cloudflare_dns_record&#34; &#34;example_cname&#34; {&#xA;  content = &#34;${cloudflare_zero_trust_tunnel_cloudflared.Production_Tunnel.id}.cfargotunnel.com&#34;&#xA;  name    = &#34;example.example.com&#34;&#xA;  proxied = true&#xA;  tags    = []&#xA;  ttl     = 1&#xA;  type    = &#34;CNAME&#34;&#xA;  zone_id = var.cloudflare_zone_id_example_com  # ここでは cloudflare_zone_example_com 変数が呼び出されている&#xA;  settings = {&#xA;    flatten_cname = false&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これらの基本設定があれば、&lt;code&gt;terraform init&lt;/code&gt; を実行して Terraform 環境を初期化し、依存関係のバージョンをロックできます。残りの部分はすべてリソースの宣言となります。&lt;/p&gt;&#xA;&lt;h2 id=&#34;既存リソースを-terraform-にインポートする&#34;&gt;既存リソースを Terraform にインポートする&#xA;&lt;/h2&gt;&lt;p&gt;前述の通り、Terraform の状態管理という概念は素晴らしいですが、初期化時には新たな問題をもたらしました——デフォルト状態では、Terraform の状態ファイルは明らかに空です。&lt;/p&gt;&#xA;&lt;p&gt;此時、私たちはクラウドサービスプロバイダー上の既存リソースの状態を Terraform にインポートする必要があります。そうすることで初めて、Terraform は私たちのインフラストラクチャをシームレスに引き継いで管理できます。皆さんも前節で Terraform リソースインポート問題に対する不満を確認したと思います。Cloudflare 上でホストされている DNS レコードのインポートを例にとると、前述のように Provider がサポートしていれば、Terraform 1.12 バージョン以降で導入された &lt;code&gt;terraform query&lt;/code&gt; を使用できますが、多くの場合、Providers はこの新機能に対応していません。Cloudflare は対応していないタイプのひとつです。&lt;/p&gt;&#xA;&lt;p&gt;幸いにも Hashi Corp. が真面目に解決しようとしなくても、Terraform を使ってインフラを管理している各社はそれぞれの知恵を出し合っています。Cloudflare は &lt;a class=&#34;link&#34; href=&#34;https://github.com/cloudflare/cf-terraforming&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;cf-terraforming&lt;/a&gt; という名のインポートツールを維持しており、多くの手動インポートの手間を省いています。まず &lt;code&gt;cf-terraforming&lt;/code&gt; をインストールします。このツールは Go 言語で書かれているため、Go 言語環境が必要です。あるいは公式にコンパイルされたバイナリファイルを &lt;code&gt;$PATH&lt;/code&gt; に配置して実行権限を与える方法もあります。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;go install github.com/cloudflare/cf-terraforming/cmd/cf-terraforming@latest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このツールの使い方は比較的シンプルです。まず以下の環境変数が必要です：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;# API Token を使用する場合&#xA;export CLOUDFLARE_API_TOKEN=&#39;Hzsq3Vub-7Y-hSTlAaLH3Jq_YfTUOCcgf22_Fs-j&#39;&#xA;&#xA;# API Key を使用する場合&#xA;export CLOUDFLARE_EMAIL=&#39;user@example.com&#39;&#xA;export CLOUDFLARE_API_KEY=&#39;1150bed3f45247b99f7db9696fffa17cbx9&#39;&#xA;&#xA;# 対象とするドメインのゾーン ID を指定。アカウントリソース（Cloudflare Tunnel など）をインポートする場合は不要&#xA;export CLOUDFLARE_ZONE_ID=&#39;81b06ss3228f488fh84e5e993c2dc17&#39;&lt;/code&gt;&lt;/pre&gt;&lt;blockquote class=&#34;alert alert-tip&#34;&gt;&#xA;        &lt;div class=&#34;alert-header&#34;&gt;&#xA;            &lt;span class=&#34;alert-icon&#34;&gt;💡&lt;/span&gt;&#xA;            &lt;span class=&#34;alert-title&#34;&gt;ヒント&lt;/span&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div class=&#34;alert-body&#34;&gt;&#xA;            &lt;p&gt;ここでのコマンドは Bash を使用していることを前提としています。Bash 構文と互換性のない Shell を使用している場合は調整が必要です。例えば Windows 上の PowerShell では、環境変数のインポート構文は次のようになります：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-pwsh&#34;&gt;$env:CLOUDFLARE_API_TOKEN=&#39;Hzsq3Vub-7Y-hSTlAaLH3Jq_YfTUOCcgf22_Fs-j&#39;&lt;/code&gt;&lt;/pre&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/blockquote&gt;&#xA;&lt;p&gt;通常は &lt;code&gt;CLOUDFLARE_API_TOKEN&lt;/code&gt; と &lt;code&gt;CLOUDFLARE_ZONE_ID&lt;/code&gt; だけ設定すれば十分です。コンソールで API Token を作成する際は、この Token に必要な権限を付与することを忘れないでください。今回は DNS レコードだけをインポートするので、ゾーン DNS の編集権限だけ付与すれば OK です。&lt;/p&gt;&#xA;&lt;p&gt;&#xA;&lt;img src=&#34;https://blog.l3zc.com/2026/04/iac-with-terraform/image-1_hu_15298d4bd366a377.webp&#34; alt=&#34;リソース操作に必要な権限を付与する&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;準備完了です。今から構成ファイルを生成しましょう：&lt;/p&gt;&#xA;&lt;p&gt;まずアカウント内のドメイン構成をインポートします。つまり &lt;code&gt;cloudflare_zone&lt;/code&gt; です：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cf-terraforming generate \&#xA;  --key $CLOUDFLARE_API_KEY \&#xA;  --resource-type &#34;cloudflare_zone&#34; &gt; zone.tf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このステップで現在のディレクトリに &lt;code&gt;zone.tf&lt;/code&gt; という名前のファイルが生成され、以下のフォーマットのコンテンツが含まれます：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hcl&#34;&gt;resource &#34;cloudflare_zone&#34; &#34;REDACTED&#34; {&#xA;  name                = &#34;REDACTED&#34;&#xA;  paused              = false&#xA;  type                = &#34;full&#34;&#xA;  vanity_name_servers = []&#xA;  account = {&#xA;    id   = &#34;REDACTED&#34;&#xA;    name = &#34;REDACTED&#34;&#xA;  }&#xA;}&#xA;&#xA;resource &#34;cloudflare_zone&#34; &#34;REDACTED&#34; {&#xA;  name                = &#34;REDACTED&#34;&#xA;  paused              = false&#xA;  type                = &#34;full&#34;&#xA;  vanity_name_servers = []&#xA;  account = {&#xA;    id   = &#34;REDACTED&#34;&#xA;    name = &#34;REDACTED&#34;&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;これでドメインリソースはインポートされましたが、中身の設定はまだありません。次に、ドメイン内の DNS レコードをインポートします：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cf-terraforming generate \&#xA;  --zone $CLOUDFLARE_ZONE_ID \&#xA;  --key $CLOUDFLARE_API_KEY \&#xA;  --resource-type &#34;cloudflare_dns_record&#34; &gt;&gt; dns.tf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このステップで現在のディレクトリに &lt;code&gt;dns.tf&lt;/code&gt; という名前の構成ファイルが生成され、以下のフォーマットのコンテンツが含まれます：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hcl&#34;&gt;resource &#34;cloudflare_dns_record&#34; &#34;terraform_managed_resource_5deb14xxxxxb629bf123xxxxxxxc8f_0&#34; {&#xA;  content  = &#34;67.24.33.108&#34;&#xA;  name     = &#34;example.example.com&#34;&#xA;  proxied  = true&#xA;  tags     = []&#xA;  ttl      = 1&#xA;  type     = &#34;A&#34;&#xA;  zone_id  = &#34;81c7f2de8dfxxxxxx52629xxxxxxfc&#34;&#xA;  settings = {}&#xA;}&#xA;&#xA;resource &#34;cloudflare_dns_record&#34; &#34;terraform_managed_resource_89xxxxx0bf9cxxxxxx9a_1&#34; {&#xA;  content  = &#34;35.27.108.33&#34;&#xA;  name     = &#34;terraform.example.com&#34;&#xA;  proxied  = true&#xA;  tags     = []&#xA;  ttl      = 1&#xA;  type     = &#34;A&#34;&#xA;  zone_id  = &#34;8xxxxxx7644e428526xxxxxx&#34;&#xA;  settings = {}&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;複数のドメインをインポートする場合は、環境変数 &lt;code&gt;CLOUDFLARE_ZONE_ID&lt;/code&gt; をそれぞれ設定し、コマンドを繰り返し実行してください。&lt;/p&gt;&#xA;&lt;p&gt;ここで生成された構成ファイルはそのまま使用可能です。これが私たちが後に必要な Terraform 構成ファイルです。しかし、現時点では単に構成ファイルが生成されただけで、Terraform の状態はまだ空のままです。ここでいきなり &lt;code&gt;terraform apply&lt;/code&gt; を実行すると、Terraform は文脈に関係なく新しくインポートした宣言をすべて新規リソースとして扱い、「Already Exists」というエラーメッセージを大量に表示します。そこで次に、生成された構成ファイルを Terraform の &lt;code&gt;terraform.tfstate&lt;/code&gt; 状態にインポートする必要があります。&lt;/p&gt;&#xA;&lt;p&gt;Terraform は 1.5 バージョンで &lt;code&gt;import&lt;/code&gt; ブロックを導入し、以前のような 1 行ずつコマンドを入力する方式よりも現代的になりました。そのインポートフローは、まず &lt;code&gt;import&lt;/code&gt; ブロックを含む &lt;code&gt;.tf&lt;/code&gt; ファイルを生成し、次回の &lt;code&gt;terraform apply&lt;/code&gt; 時に Terraform が自動的にインポート操作を実行します。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;cloudflare_zone&lt;/code&gt; の &lt;code&gt;import&lt;/code&gt; ブロックを生成：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cf-terraforming import \&#xA;  --resource-type &#34;cloudflare_zone&#34; \&#xA;  --modern-import-block \&#xA;  --key $CLOUDFLARE_API_KEY \&#xA;  --zone $CLOUDFLARE_ZONE_ID &gt;&gt; import.tf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;cloudflare_dns_record&lt;/code&gt; の &lt;code&gt;import&lt;/code&gt; ブロックを生成：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cf-terraforming import \&#xA;  --resource-type &#34;cloudflare_dns_record&#34; \&#xA;  --modern-import-block \&#xA;  --key $CLOUDFLARE_API_KEY \&#xA;  --zone $CLOUDFLARE_ZONE_ID &gt;&gt; import.tf&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;このステップで現在のディレクトリに &lt;code&gt;import.tf&lt;/code&gt; が生成されます。必要なインポート情報が含まれており、役割は Terraform に上一步で生成された各 &lt;code&gt;resource&lt;/code&gt; ブロックが、どのクラウドプロバイダーのリソース ID に対応するのかを教えることです。この ID はクラウドプロバイダー内部でリソースをマークするコードであり、普段はコントロールパネルに表示されません。API で特別にリクエストしない限り知ることはできません。Terraform はインポートプロセス中にこの ID を使用して、ローカルの定義がクラウド上のどのリソースに対応しているかを確認し、厳密な冪等性を実現します。&lt;/p&gt;&#xA;&lt;p&gt;さて、ここでは &lt;code&gt;terraform plan&lt;/code&gt; を実行します：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;$ terraform plan&#xA;cloudflare_dns_record.minio_a: Refreshing state... [id=xxxxxxxxxxx53]&#xA;cloudflare_zero_trust_tunnel_cloudflared_config.raspberrypi: Refreshing state...&#xA;......&#xA;&#xA;Terraform will perform the following actions:&#xA;&#xA;  # cloudflare_dns_record.terraform_managed_resource_0 will be imported&#xA;    resource &#34;cloudflare_dns_record&#34; &#34;terraform_managed_resource_REDACTED_0&#34; {&#xA;        content     = &#34;67.24.33.108&#34;&#xA;        created_on  = &#34;2026-04-08T10:18:12Z&#34;&#xA;        id          = &#34;5deb14c21xxxxxxx20f1c8f&#34;&#xA;        meta        = jsonencode({})&#xA;        modified_on = &#34;2026-04-08T10:18:12Z&#34;&#xA;        name        = &#34;example.example.com&#34;&#xA;        proxiable   = true&#xA;        proxied     = true&#xA;        settings    = {}&#xA;        tags        = []&#xA;        ttl         = 1&#xA;        type        = &#34;A&#34;&#xA;        zone_id     = &#34;REDACTED&#34;&#xA;    }&#xA;&#xA;  # cloudflare_dns_record.terraform_managed_resource_1 will be imported&#xA;    resource &#34;cloudflare_dns_record&#34; &#34;terraform_managed_resource_89c149exxxxxxxxxxxba13xxxxxa_1&#34; {&#xA;        content     = &#34;35.27.108.33&#34;&#xA;        created_on  = &#34;2026-04-08T10:17:54Z&#34;&#xA;        id          = &#34;89cxxxxxxxxxxxxxxxxxx09a&#34;&#xA;        meta        = jsonencode({})&#xA;        modified_on = &#34;2026-04-08T10:17:54Z&#34;&#xA;        name        = &#34;terraform.example.com&#34;&#xA;        proxiable   = true&#xA;        proxied     = true&#xA;        settings    = {}&#xA;        tags        = []&#xA;        ttl         = 1&#xA;        type        = &#34;A&#34;&#xA;        zone_id     = &#34;81xxxxxxxxxxxxxxxxxxxxxfc&#34;&#xA;    }&#xA;&#xA;Plan: 2 to import, 0 to add, 0 to change, 0 to destroy.&#xA;&#xA;────────────────────────────────────────────────────────────────────────────────────────────────────────&#xA;&#xA;Note: You didn&#39;t use the -out option to save this plan, so Terraform can&#39;t guarantee to take exactly&#xA;these actions if you run &#34;terraform apply&#34; now.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add、Change、Destroy のリソース数がすべて 0 の場合、インポート操作に問題がないことになります。そのまま &lt;code&gt;terraform apply --auto-approve&lt;/code&gt; を実行すれば、リソースのインポート完了です。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;$ terraform apply --auto-approve&#xA;cloudflare_dns_record.push_a: Refreshing state... [id=REDACTED]&#xA;&#xA;Terraform will perform the following actions:&#xA;&#xA;  # cloudflare_dns_record.terraform_managed_resource_REDACTED_0 will be imported&#xA;    resource &#34;cloudflare_dns_record&#34; &#34;terraform_managed_resource_REDACTED_0&#34; {&#xA;        content     = &#34;67.24.33.108&#34;&#xA;        created_on  = &#34;2026-04-08T10:18:12Z&#34;&#xA;        id          = &#34;REDACTED&#34;&#xA;        meta        = jsonencode({})&#xA;        modified_on = &#34;2026-04-08T10:18:12Z&#34;&#xA;        name        = &#34;example.example.com&#34;&#xA;        proxiable   = true&#xA;        proxied     = true&#xA;        settings    = {}&#xA;        tags        = []&#xA;        ttl         = 1&#xA;        type        = &#34;A&#34;&#xA;        zone_id     = &#34;REDACTED&#34;&#xA;    }&#xA;&#xA;  # cloudflare_dns_record.terraform_managed_resource_REDACTED_1 will be imported&#xA;    resource &#34;cloudflare_dns_record&#34; &#34;terraform_managed_resource_REDACTED_1&#34; {&#xA;        content     = &#34;35.27.108.33&#34;&#xA;        created_on  = &#34;2026-04-08T10:17:54Z&#34;&#xA;        id          = &#34;REDACTED&#34;&#xA;        meta        = jsonencode({})&#xA;        modified_on = &#34;2026-04-08T10:17:54Z&#34;&#xA;        name        = &#34;terraform.example.com&#34;&#xA;        proxiable   = true&#xA;        proxied     = true&#xA;        settings    = {}&#xA;        tags        = []&#xA;        ttl         = 1&#xA;        type        = &#34;A&#34;&#xA;        zone_id     = &#34;REDACTED&#34;&#xA;    }&#xA;&#xA;Plan: 2 to import, 0 to add, 0 to change, 0 to destroy.&#xA;cloudflare_dns_record.terraform_managed_resource_REDACTED_1: Importing... [id=REDACTED/REDACTED]&#xA;cloudflare_dns_record.terraform_managed_resource_REDACTED_1: Import complete [id=REDACTED/REDACTED]&#xA;cloudflare_dns_record.terraform_managed_resource_REDACTED_0: Importing... [id=REDACTED/REDACTED]&#xA;cloudflare_dns_record.terraform_managed_resource_REDACTED_0: Import complete [id=REDACTED/REDACTED]&#xA;&#xA;Apply complete! Resources: 2 imported, 0 added, 0 changed, 0 destroyed.&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;状態保存と継続的インテグレーション&#34;&gt;状態保存と継続的インテグレーション&#xA;&lt;/h2&gt;&lt;p&gt;IaC の重要な価値の一つは、Git ベースの複数人コラボレーションと CI の継続的インテグレーションを容易に実現できる点ですが、その前に解決すべき新たな問題があります——&lt;code&gt;terraform.tfstate&lt;/code&gt;はどこに置くか：環境を変更するたびに辛苦入れてインポートした状態を失う人は誰も望んでいません。&lt;/p&gt;&#xA;&lt;p&gt;Terraform は現在、以下の状態保存バックエンドをサポートしています：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;local&lt;/li&gt;&#xA;&lt;li&gt;remote&lt;/li&gt;&#xA;&lt;li&gt;azurerm&lt;/li&gt;&#xA;&lt;li&gt;consul&lt;/li&gt;&#xA;&lt;li&gt;cos&lt;/li&gt;&#xA;&lt;li&gt;gcs&lt;/li&gt;&#xA;&lt;li&gt;http&lt;/li&gt;&#xA;&lt;li&gt;Kubernetes&lt;/li&gt;&#xA;&lt;li&gt;oci&lt;/li&gt;&#xA;&lt;li&gt;oss&lt;/li&gt;&#xA;&lt;li&gt;pg&lt;/li&gt;&#xA;&lt;li&gt;s3&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;特別な要件がなければ、私のように &lt;code&gt;s3&lt;/code&gt; を選択することをお勧めします。Cloudflare R2 には無料枠があるので、使わない手はありません。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-hcl&#34;&gt;terraform {&#xA;  backend &#34;s3&#34; {&#xA;    bucket = &#34;terraform&#34;&#xA;    key    = &#34;terraform.tfstate&#34;&#xA;    region = &#34;auto&#34;&#xA;    endpoints = {&#xA;      s3 = &#34;https://REDACTED.r2.cloudflarestorage.com&#34;&#xA;    }&#xA;&#xA;    # R2 にはこれらの AWS 認証は不要&#xA;    skip_credentials_validation = true&#xA;    skip_metadata_api_check     = true&#xA;    skip_region_validation      = true&#xA;    skip_requesting_account_id  = true&#xA;    use_path_style              = true&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;S3 バックエンドの場合、Credentials を保存するには &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; と &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; の 2 つの環境変数を使用することをお勧めします：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;export AWS_ACCESS_KEY_ID=&#39;REDACTED&#39;&#xA;export AWS_SECRET_ACCESS_KEY=&#39;REDACTED&#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;設定完了後、&lt;code&gt;terraform init -migrate-state&lt;/code&gt; を実行すれば、設定はクラウド上に正常に保存されます。今後どこで設定を変更しても、&lt;code&gt;terraform apply&lt;/code&gt; を実行しても、Terraform の State が同期していないことを心配する必要はありません。&lt;/p&gt;&#xA;&lt;p&gt;続いて Github CI の設定ですが、実はとてもシンプルです。&lt;code&gt;git push&lt;/code&gt; のたびに &lt;code&gt;terraform init&lt;/code&gt;、&lt;code&gt;terraform fmt&lt;/code&gt;、&lt;code&gt;terraform apply&lt;/code&gt; をトリガーするだけです。以下が私の &lt;code&gt;.github/workflows/apply.yml&lt;/code&gt; です：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: &#34;Terraform Apply&#34;&#xA;&#xA;on:&#xA;  push:&#xA;    branches:&#xA;      - main&#xA;&#xA;env:&#xA;  TF_IN_AUTOMATION: &#34;true&#34;&#xA;  CLOUDFLARE_API_TOKEN: &#34;${{ secrets.CLOUDFLARE_API_TOKEN }}&#34;&#xA;  AWS_ACCESS_KEY_ID: &#34;${{ secrets.AWS_ACCESS_KEY_ID }}&#34;&#xA;  AWS_SECRET_ACCESS_KEY: &#34;${{ secrets.AWS_SECRET_ACCESS_KEY }}&#34;&#xA;  TF_VAR_cloudflare_zone_id_example_com: &#34;${{ vars.TF_VAR_CLOUDFLARE_ZONE_ID_EXAMPLE_COM }}&#34;&#xA;  TF_VAR_cloudflare_zone_id_example_top: ${{ vars.TF_VAR_CLOUDFLARE_ZONE_ID_EXAMPLE_TOP }}&#xA;&#xA;jobs:&#xA;  terraform:&#xA;    name: &#34;Terraform Apply&#34;&#xA;    runs-on: ubuntu-latest&#xA;    permissions:&#xA;      contents: read&#xA;    concurrency:&#xA;      group: terraform-apply&#xA;      cancel-in-progress: false&#xA;    steps:&#xA;      - name: Checkout&#xA;        uses: actions/checkout@v6&#xA;&#xA;      - name: Setup Terraform&#xA;        uses: hashicorp/setup-terraform@v4&#xA;&#xA;      - name: Terraform Init&#xA;        run: terraform init -input=false&#xA;&#xA;      - name: Terraform Apply&#xA;        run: terraform apply -input=false -auto-approve&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;PR に対しては、CI が毎回 PR に &lt;code&gt;terraform plan&lt;/code&gt; の出力を添付するように設定すべきです：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;name: Terraform Plan&#xA;&#xA;on:&#xA;  pull_request:&#xA;    paths:&#xA;      - &#34;**/*.tf&#34;&#xA;      - &#34;.github/workflows/terraform-plan.yml&#34;&#xA;&#xA;permissions:&#xA;  contents: read&#xA;  pull-requests: write&#xA;&#xA;env:&#xA;  TF_IN_AUTOMATION: &#34;true&#34;&#xA;  CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}&#xA;  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}&#xA;  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}&#xA;  TF_VAR_cloudflare_zone_id_example_com: &#34;${{ vars.TF_VAR_CLOUDFLARE_ZONE_ID_EXAMPLE_COM }}&#34;&#xA;  TF_VAR_cloudflare_zone_id_example_top: ${{ vars.TF_VAR_CLOUDFLARE_ZONE_ID_EXAMPLE_TOP }}&#xA;&#xA;jobs:&#xA;  plan:&#xA;    name: Terraform Plan&#xA;    runs-on: ubuntu-latest&#xA;    steps:&#xA;      - uses: actions/checkout@v6&#xA;&#xA;      - uses: hashicorp/setup-terraform@v4&#xA;&#xA;      - name: Terraform fmt&#xA;        id: fmt&#xA;        run: terraform fmt -check -recursive&#xA;        continue-on-error: true&#xA;&#xA;      - name: Terraform Init&#xA;        id: init&#xA;        run: terraform init -input=false&#xA;&#xA;      - name: Terraform Validate&#xA;        id: validate&#xA;        run: terraform validate -no-color&#xA;&#xA;      - name: Terraform Plan&#xA;        id: plan&#xA;        run: terraform plan -input=false -no-color&#xA;        continue-on-error: true&#xA;&#xA;      - name: Post Plan to PR&#xA;        uses: actions/github-script@v8&#xA;        with:&#xA;          github-token: ${{ secrets.GITHUB_TOKEN }}&#xA;          script: |&#xA;            const { data: comments } = await github.rest.issues.listComments({&#xA;              owner: context.repo.owner,&#xA;              repo: context.repo.repo,&#xA;              issue_number: context.issue.number,&#xA;            });&#xA;            const botComment = comments.find(c =&gt;&#xA;              c.user.type === &#39;Bot&#39; &amp;&amp; c.body.includes(&#39;&lt;!-- terraform-plan --&gt;&#39;)&#xA;            );&#xA;&#xA;            const planOutput = `${{ steps.plan.outputs.stdout }}`.substring(0, 65000);&#xA;&#xA;            const body = `&lt;!-- terraform-plan --&gt;&#xA;            #### Terraform Plan&#xA;&#xA;            | Step     | Result                            |&#xA;            | -------- | --------------------------------- |&#xA;            | fmt      | \`${{ steps.fmt.outcome }}\`      |&#xA;            | init     | \`${{ steps.init.outcome }}\`     |&#xA;            | validate | \`${{ steps.validate.outcome }}\` |&#xA;            | plan     | \`${{ steps.plan.outcome }}\`     |&#xA;&#xA;            &lt;details&gt;&lt;summary&gt;Plan 詳細を展開&lt;/summary&gt;&#xA;&#xA;            \`\`\`terraform&#xA;            ${planOutput}&#xA;            \`\`\`&#xA;            &lt;/details&gt;`;&#xA;&#xA;            if (botComment) {&#xA;              await github.rest.issues.updateComment({&#xA;                owner: context.repo.owner,&#xA;                repo: context.repo.repo,&#xA;                comment_id: botComment.id,&#xA;                body&#xA;              });&#xA;            } else {&#xA;              await github.rest.issues.createComment({&#xA;                issue_number: context.issue.number,&#xA;                owner: context.repo.owner,&#xA;                repo: context.repo.repo,&#xA;                body&#xA;              });&#xA;            }&#xA;&#xA;      - name: Fail if plan failed&#xA;        if: steps.plan.outcome == &#39;failure&#39;&#xA;        run: exit 1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&#xA;&lt;img src=&#34;https://blog.l3zc.com/2026/04/iac-with-terraform/image-2_hu_c922b7d4de19a009.webp&#34; alt=&#34;毎回 PR には Plan の出力がある&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&lt;h2 id=&#34;今後のワークフロー&#34;&gt;今後のワークフロー&#xA;&lt;/h2&gt;&lt;p&gt;ここまでで、Terraform の「接管初期化」は終了し、日常保守フェーズに入りました。この段階では実際には 3 つのことしかありません：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;新しいリソースの作成&lt;/li&gt;&#xA;&lt;li&gt;既存リソースの変更&lt;/li&gt;&#xA;&lt;li&gt;不要になったリソースの削除&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;よく使う操作は 2 つ：&lt;code&gt;terraform plan&lt;/code&gt; と &lt;code&gt;terraform apply&lt;/code&gt;。個人利用の場合、小さな変更はそのままコミットしても良いでしょう。チームで協力する場合、毎回変更は PR を通じた方が原則です。&lt;/p&gt;&#xA;&lt;h3 id=&#34;インフラストラクチャの作成&#34;&gt;インフラストラクチャの作成&#xA;&lt;/h3&gt;&lt;p&gt;今 DNS レコードを追加したり、Tunnel やオブジェクトストレージバケットを新しく作る場合のフローは以下の通りです：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-mermaid&#34;&gt;flowchart TD&#xA;  C1[ブランチ作成&#xA;  例：feat/add-minio-record] --&gt; C2[resource ブロックを追加]&#xA;  C2 --&gt; C3[terraform fmt + validate]&#xA;  C3 --&gt; C4[terraform plan]&#xA;  C4 --&gt; C5{予期したリソースのみが追加されたか？}&#xA;  C5 -- いいえ --&gt; C6[設定を修正して plan を再実行]&#xA;  C6 --&gt; C4&#xA;  C5 -- はい --&gt; C7[PR を提出]&#xA;  C7 --&gt; C8[マージ後 CI apply]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;最も理想的な &lt;code&gt;plan&lt;/code&gt; 出力は：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;X to add&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;0 to change&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;0 to destroy&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;何かを追加したいだけなのに &lt;code&gt;to destroy&lt;/code&gt; が表示された場合は、焦らずにチェックしてください。引用ミスタイプ、変数間違い、またはリソースアドレスのうっかり変更などが原因であることがほとんどです。どこに問題があるのか仔細に確認してください。&lt;/p&gt;&#xA;&lt;h3 id=&#34;修改と删除基础设施&#34;&gt;修改と删除基础设施&#xA;&lt;/h3&gt;&lt;p&gt;変更フローは作成と似ていますが、もう一つのステップが増えます——変更が再構築を引き起こすかどうかの評価です。&lt;/p&gt;&#xA;&lt;p&gt;多くの Provider フィールドは &lt;code&gt;ForceNew&lt;/code&gt; となっており、フィールドを変えるつもりが Terraform は「了解、削除して再構築します」と言ってくるかもしれません。DNS のようなリソースでは大きな問題ではありませんが、クラウドインスタンスのようなリソースになると、削除して再構築すれば損失が発生します。&lt;/p&gt;&#xA;&lt;p&gt;以下の順序で進めることをお勧めします：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-mermaid&#34;&gt;flowchart TD&#xA;  M1[.tf 修正] --&gt; M2[terraform plan]&#xA;  M2 --&gt; M3{replace/destroy 出現？}&#xA;  M3 -- いいえ --&gt; M7[影響範囲を確認]&#xA;  M7 --&gt; M8[terraform apply]&#xA;  M3 -- はい --&gt; M4[停止して変更内容を見直す]&#xA;  M4 --&gt; M5[必要に応じて lifecycle 保護を追加]&#xA;  M5 --&gt; M6[変更ウィンドウを計画]&#xA;  M6 --&gt; M8&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;本番環境にとって、作成や変更が遅くても大きな問題ではありません。重要なのは操作の正確性です。少し遅くても、間違えないようにしてください。IaC はスピードを競うのではなく、予測可能性を競うものです。&lt;/p&gt;&#xA;&lt;p&gt;もしリソースを削除する場合（例えば DNS レコードの廃止、废弃 Tunnel の整理）、以下のフローに従ってください&lt;sup id=&#34;fnref:6&#34;&gt;&lt;a href=&#34;#fn:6&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;6&lt;/a&gt;&lt;/sup&gt;：&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-mermaid&#34;&gt;flowchart TD&#xA;  D1[リソース廃止の確認、業務/監視/スクリプト依存チェック] --&gt; D2[resource ブロック削除、または count/for_each 調整]&#xA;  D2 --&gt; D3[terraform plan]&#xA;  D3 --&gt; D4{to destroy が予期通り？}&#xA;  D4 -- いいえ --&gt; D5[変更をロールバックし依存関係を調査継続]&#xA;  D5 --&gt; D1&#xA;  D4 -- はい --&gt; D6[ロールバックプランを用意し、低負荷時間帯を選択]&#xA;  D6 --&gt; D7[PR 審査通過]&#xA;  D7 --&gt; D8[terraform apply]&#xA;  D8 --&gt; D9[削除後の可用性チェック]&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;日常のコラボレーション提案&#34;&gt;日常のコラボレーション提案&#xA;&lt;/h3&gt;&lt;ul&gt;&#xA;&lt;li&gt;Credentials は環境変数や CI Secret に置き、&lt;code&gt;.tf&lt;/code&gt; やリポジトリに書き込まない&lt;/li&gt;&#xA;&lt;li&gt;重要なリソースには保護ポリシーを有効にし、誤削除を防ぐ&lt;/li&gt;&#xA;&lt;li&gt;ディレクトリをリソースタイプごとに分割する&lt;/li&gt;&#xA;&lt;li&gt;定期的に &lt;code&gt;terraform plan&lt;/code&gt; を実行してインフラストラクチャドリフトをチェック&lt;sup id=&#34;fnref:7&#34;&gt;&lt;a href=&#34;#fn:7&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;7&lt;/a&gt;&lt;/sup&gt;し是正し、パネルでの誤操作による手動変更を避ける&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;このフローはとても面倒に見えますが、毎回の变更に記録があり、監査可能で、ロールバック可能、何より再現可能である点が、IaC が最も価値のある部分です。&lt;/p&gt;&#xA;&lt;h2 id=&#34;参考&#34;&gt;参考&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://candinya.com/posts/manage-cloudflare-dns-with-terraform/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Terraform を使って CloudFlare 上の DNS 解析レコードを管理する - Candinya&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developer.hashicorp.com/terraform/tutorials/automation/github-actions&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Automate Terraform with GitHub Actions&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developer.hashicorp.com/terraform/language/files/tfquery&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Query configuration files&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developer.hashicorp.com/terraform/language/block/tfquery/list&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;list block reference&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/cloudflare/cf-terraforming&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;cloudflare/cf-terraforming&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.cloudflare.com/terraform/advanced-topics/import-cloudflare-resources/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Import Cloudflare resources&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Cloudflare Provider - Terraform Registry&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_code&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Infrastructure as Code&lt;/a&gt;、機械可読な構成ファイルを使用して必要なインフラストラクチャのデプロイ方法を定義することを指します。&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:2&#34;&gt;&#xA;&lt;p&gt;各プロバイダーの構成ファイルフィールド名や形式には違いがありますが、これはスクリプトを書くことで容易に変換できます。&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:3&#34;&gt;&#xA;&lt;p&gt;特に注意すべき点は、状態ファイルにはデータベースのパスワードや API キーなど、プレーンテキストで保存される機密情報が含まれる可能性があるため、絶対に &lt;code&gt;.tfstate&lt;/code&gt; ファイルを公開コードリポジトリにアップロードしないことです。&amp;#160;&lt;a href=&#34;#fnref:3&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:4&#34;&gt;&#xA;&lt;p&gt;HashiCorp Configuration Language。HashiCorp 自社開発の宣言型構成言語で、機械可読性と人間可読性の両立を目指している。&amp;#160;&lt;a href=&#34;#fnref:4&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:5&#34;&gt;&#xA;&lt;p&gt;冪等性（Idempotence）とは、コンピュータシステムまたはインターフェースが同一の要求を複数回受け取った場合、影響が単回実行の結果と同じになる性質のこと。何度実行しても、システムの最終状態は常に一致する。&amp;#160;&lt;a href=&#34;#fnref:5&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:6&#34;&gt;&#xA;&lt;p&gt;Terraform にリソースの管理から外れさせたいだけで、クラウド上で実際に削除したいわけではない場合は、&lt;code&gt;terraform state rm&lt;/code&gt; コマンドを使用してください。コードからリソースブロックを削除して &lt;code&gt;apply&lt;/code&gt; するのは避けてください。そうしないとクラウド上の実際のリソースも同時に削除されてしまいます。&amp;#160;&lt;a href=&#34;#fnref:6&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li id=&#34;fn:7&#34;&gt;&#xA;&lt;p&gt;インフラストラクチャドリフト（Infrastructure Drift）とは、現実世界中でコンソールのクリックなど IaC 以外の経路でインフラストラクチャに変更を加え、実際の状態とコード内で宣言された状態が不一致になる現象のこと。&amp;#160;&lt;a href=&#34;#fnref:7&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;</description>
        </item></channel>
</rss>
