☁️

CDKTFで Azure も AWS みたいにやれるのでは?」という夢と、その現実

に公開

はじめに:CDKTFで AzureのIaC は、まだ早い、やめとけw

AWS CDK の美しさを目にして、「Azure でも同じように CDKTF でやろうぜ」と考えたことはありませんか?

気持ちはよくわかります。TypeScript でインフラを書ける快感プログラミング言語のパワーを使える自由度。AWS CDK はそれを実現しています。

しかし、Azure で CDKTF を本番環境で運用しようとすると、想像以上に厳しい現実に直面します。

本記事では、HCL 既習者であるあなただからこそ見えるべき、CDKTF for Azure の限界を率直に解説します。その上で、Azure で「本当に効率的」な IaC を選ぶための意思決定フローを提供します。


1. なぜ CDKTF が魅力的に見えるのか

AWS CDK の成功が生んだ期待値

AWS CDK がここまで成功した理由は単純です。

// AWS CDK: プログラミング言語のパワーを活用
const app = new cdk.App();
const stack = new cdk.Stack(app, 'MyStack');

// ループで複数のリソースを生成
for (let i = 0; i < 3; i++) {
  new s3.Bucket(stack, `Bucket${i}`, {
    bucketName: `my-bucket-${i}`,
    versioned: true,
  });
}

// 高レベルコンストラクトで複雑性を隠蔽
new ecs.ApplicationLoadBalancedFargateService(stack, 'Service', {
  // 20+ のリソースが自動的に構築される
});

この開発体験は CloudFormation の JSON を手書きする時代から見ると、革命的です。

Azure でも同じことができるはずでは?

CDKTF(CloudFormation Development Kit for Terraform)は、この CDK の思想を Terraform に適用したもの。

「ならば Azure でも...」という考えは論理的に正当です。しかし現実は異なります。


2. 厳しい現実:CDKTF for Azure の 5 つの問題

問題 1:更新スローペース

2023年    : 月平均 1-2 回の GitHub 更新
2024年    : 月平均 0.5-1 回の更新(スローダウン中)
2025年    : 不定期更新(低優先度に)

比較:
  Bicep    : 毎週のようにアップデート
  Terraform: 月 2-3 回のリリース
  CDKTF    : ほぼ停滞状態

この差が何を意味するか?Azure が新サービスをリリースしたとき、CDKTF ではそれが 3-6 ヶ月対応されない可能性があります


問題 2:L1 コンストラクトのみで、高レベル抽象化がほぼ不在

AWS CDK には L1(Construct Level 1)→ L2 → L3 という層がありますが、CDKTF for Azure には事実上 L1 のみです。

// 🔴 CDKTF for Azure: 手動でゴリゴリ書く必要がある
import { AzurermProvider, VirtualNetwork, Subnet, 
         NetworkInterface, ResourceGroup, LinuxVirtualMachine } 
  from "./.gen/providers/azurerm";

class AzureStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);
    
    // リソースグループ
    const rg = new ResourceGroup(this, "rg", {
      name: "my-rg",
      location: "eastus",
    });
    
    // VNet
    const vnet = new VirtualNetwork(this, "vnet", {
      name: "my-vnet",
      location: rg.location,
      resourceGroupName: rg.name,
      addressSpace: ["10.0.0.0/16"],
    });
    
    // サブネット(手動で作成)
    const subnet = new Subnet(this, "subnet", {
      name: "my-subnet",
      resourceGroupName: rg.name,
      virtualNetworkName: vnet.name,
      addressPrefixes: ["10.0.1.0/24"],
    });
    
    // NIC(手動で作成)
    const nic = new NetworkInterface(this, "nic", {
      name: "my-nic",
      resourceGroupName: rg.name,
      location: rg.location,
      ipConfiguration: [{
        name: "internal",
        subnetId: subnet.id,
        privateIpAddressAllocation: "Dynamic",
      }],
    });
    
    // VM(やっと)
    const vm = new LinuxVirtualMachine(this, "vm", {
      name: "my-vm",
      location: rg.location,
      resourceGroupName: rg.name,
      vmSize: "Standard_B2s",
      adminUsername: "azureuser",
      networkInterfaceIds: [nic.id],
      osDisk: {
        caching: "ReadWrite",
        storageAccountType: "Premium_LRS",
      },
      sourceImageReference: {
        publisher: "Canonical",
        offer: "0001-com-ubuntu-server-jammy",
        sku: "22_04-lts-gen2",
        version: "latest",
      },
    });
  }
}

比較:AWS CDK なら

// 🟢 AWS CDK: 高レベルコンストラクトで実現
import * as ec2 from 'aws-ec2';
import * as cdk from 'aws-cdk-lib';

export class MyStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string) {
    super(scope, id);
    
    const vpc = new ec2.Vpc(this, 'VPC', {
      cidrMask: 24,
      natGateways: 1,
    });
    
    const instance = new ec2.Instance(this, 'Instance', {
      vpc,
      instanceType: ec2.InstanceType.of(
        ec2.InstanceClass.T2,
        ec2.InstanceSize.MICRO,
      ),
      machineImage: new ec2.AmazonLinuxImage(),
    });
  }
}

AWS CDK は 15 行、CDKTF は 50 行以上。これが CDKTF の現実です。


問題 3:プリビルドパッケージが古い

公式が提供する cdktf-azure-providers パッケージを使うと:

期待: azurerm v3.x (最新)
現実: azurerm v2.x が提供される

新しい API 機能が使いたい?
→ 手動で `cdktf generate` コマンドで再生成
→ 依存関係の衝突リスク
→ デバッグ地獄へようこそ

問題 4:属人化リスクが「本番運用では許されない」レベル

CDKTF で複雑なロジックを書いた TypeScript/Python コードを保守できるメンバーが去ったとき、あなたは詰みます

// 例:動的にリソース生成する複雑なロジック
const environments = ['dev', 'staging', 'prod'];
const configs = loadConfigsFromYAML();

environments.forEach(env => {
  configs[env].instances.forEach((instance, idx) => {
    // 複雑な条件分岐...
    if (instance.type === 'compute' && configs[env].enableAccelerator) {
      // GPU 付き VM を作成
    } else {
      // 通常 VM を作成
    }
  });
});

このコードを見たとき:

  • HCL なら「リソースの定義を読むだけ」で動作が予測可能
  • CDKTF だと「TypeScript のロジック全体を理解する必要がある」

本番環境の障害時に 3 時間悩むよりも、明確に読めるコードがどれほど価値か、あなたなら理解しているはずです。


問題 5:本番トラブル時のサポート情報が圧倒的に不足

Google で「CDKTF Azure xxxx」を検索してみてください。

Stack Overflow の回答:       0-1 件
GitHub Issues の similar:    2-3 件
Zenn/Qiita の記事:          ほぼなし
ブログ記事:                 企業ブログ数件のみ

比較:「Bicep xxxx」
Stack Overflow:          数十件
Microsoft Learn:         公式ドキュメント充実
日本語記事:              数百件

本番障害時に、公式ドキュメントだけが頼りという状況は、組織にとってリスクです。


3. では何を使うべきなのか?Azure 単体なら「Bicep」

Bicep が最強な理由

Bicep は Microsoft 公式の Azure Infrastructure as Code 言語。Azure Resource Manager に直接統合されており、Azure が状態を管理します

// Bicep: シンプル、読みやすい
param location string = 'japaneast'
param environment string

resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = {
  name: 'rg-${environment}'
  location: location
}

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-09-01' = {
  name: 'vnet-${environment}'
  location: location
  parent: resourceGroup
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'default'
        properties: {
          addressPrefix: '10.0.1.0/24'
        }
      }
    ]
  }
}

resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' = {
  name: 'vm-${environment}'
  location: location
  parent: resourceGroup
  // ... configuration
}

Bicep vs Terraform HCL vs CDKTF 実装比較

同じ「ストレージアカウント」を 3 つのツールで実装してみましょう。

Bicep:

param storageAccountName string
param location string

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

output accountId string = storageAccount.id

行数:14 行


Terraform HCL:

variable "storage_account_name" {
  type = string
}

variable "location" {
  type = string
}

resource "azurerm_storage_account" "example" {
  name                     = var.storage_account_name
  resource_group_name      = azurerm_resource_group.example.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  account_kind             = "StorageV2"
}

output "account_id" {
  value = azurerm_storage_account.example.id
}

行数:21 行


CDKTF for Azure (TypeScript):

import { Construct } from 'constructs';
import { TerraformStack, TerraformVariable } from 'cdktf';
import { AzurermProvider, StorageAccount, ResourceGroup } 
  from './.gen/providers/azurerm';

interface StorageAccountConfig {
  storageAccountName: string;
  location: string;
  resourceGroupName: string;
}

export class StorageAccountStack extends TerraformStack {
  constructor(scope: Construct, id: string, config: StorageAccountConfig) {
    super(scope, id);

    new AzurermProvider(this, 'azure', {
      features: {},
    });

    const resourceGroup = new ResourceGroup(this, 'rg', {
      name: config.resourceGroupName,
      location: config.location,
    });

    const storageAccount = new StorageAccount(this, 'sa', {
      name: config.storageAccountName,
      resourceGroupName: resourceGroup.name,
      location: resourceGroup.location,
      accountTier: 'Standard',
      accountReplicationType: 'LRS',
      accountKind: 'StorageV2',
    });

    // Output
    new TerraformOutput(this, 'account_id', {
      value: storageAccount.id,
    });
  }
}

行数:40 行+(定義ファイル別途)


現実的な選択ガイド

状況 推奨 理由
Azure のみ、HCL 経験あり Bicep 学習期間 1-2 週間、保守性最高
マルチクラウド戦略あり Terraform HCL 統一的な運用フロー
AWS CDK やっているから同じで Terraform HCL (CDKTF は❌) HCL も 2-3 週間で習得可能
複雑な動的ロジック必須 Terraform HCL CDKTF はリソース不足
既に CDKTF で小規模本番運用中 Bicep または HCL へ移行を推奨 リスク軽減

4. 意思決定フロー

START
  │
  ├─ Q1: Azure のみか?マルチクラウドか?
  │
  │  Azure のみ
  │  │
  │  ├─ Q2: チーム的に複雑なプログラミングロジック必須か?
  │  │
  │  │  必須でない(ほとんどの場合)
  │  │  │
  │  │  └─ 🏆 Bicep を推奨
  │  │     ✅ 学習期間 1-2 週間
  │  │     ✅ 保守性最高
  │  │     ✅ ドキュメント充実
  │  │
  │  必須(複雑な動的リソース生成など)
  │  │
  │  └─ 🏆 Terraform HCL を推奨
  │     ✅ for_each, dynamic blocks で複雑ロジック対応
  │     ✅ カスタムモジュール化可能
  │
  マルチクラウド
  │
  └─ 🏆 Terraform HCL のみ
     ✅ AWS, GCP, Azure 統一運用
     ✅ DataDog, Okta など SaaS 統合可能

5. HCL 経験者だからこそ見えること

あなたが HCL を 1 年以上経験しているなら、実はTerraform HCL はもう習得している状態です。

# 基本は同じ
resource "azurerm_resource_group" "example" {
  name     = var.name
  location = var.location
}

resource "azurerm_virtual_network" "example" {
  name                = var.vnet_name
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
}

構文も思想も AWS(EC2、VPC)の HCL と全く同じ。1 週間もあれば Azure リソースの書き方に慣れます。

一方、CDKTF は新しい概念を 3-4 週間かけて学ぶ必要があります。それでいて得られるメリットは「複雑な場合に TypeScript が使える」だけ。


6. 本番環境への責任

エンタープライズの本番環境で運用するインフラストラクチャを選択する際は、技術の美しさよりも「運用可能性」が優先されるべきです。

チェックリスト

□ ドキュメント:公式・日本語・サードパーティが充実している
□ サポート:問題発生時に Stack Overflow や GitHub Issues で即座に答えが見つかる
□ チーム:メンバーが去っても誰かが保守できる(属人化リスク最小)
□ 更新:新しい Azure 機能に 1-2 ヶ月以内に対応される
□ 運用:障害時に root cause を素早く特定できる透明性がある

CDKTF は 5 つのうち 4 つが ❌ です。


7. それでも CDKTF を選ぶべき場面

まさかの例外ケースですが、存在します。

ケース 1:既に AWS CDK で全社的に統一されている

// アプリ: AWS CDK (TypeScript)
// インフラ: AWS CDK (TypeScript)
// 新規: Azure も同じ SDK で...

この場合のみ、CDKTF 採用検討価値があります
(ただし長期的には Bicep への移行を推奨)

ケース 2:JSON ジェネレータとしての活用

# CDKTF で Terraform JSON を生成
cdktf synth

# 出力された .tf.json を terraform で実行
terraform plan
terraform apply

この使い方なら CDKTF の不安定性の影響を最小化できます。


まとめ:CDKTF on Azureに夢見る皆さんへのメッセージ

AWS CDK の美しさはわかります。プログラミング言語のパワーでインフラを表現する快感も、よく理解しています。

しかし、Azure で本番環境を運用する責任を背負ったとき、CDKTF は期待値と現実のギャップが大きすぎます

代わりに:

  1. Azure のみなら Bicep

    • 学習期間:1-2 週間
    • 保守性:最高
    • リスク:最小
  2. マルチクラウド戦略なら Terraform HCL

    • 学習期間:2-3 週間
    • 保守性:高い
    • 長期的価値:大

どちらも、あなたの HCL スキルは直接活用できます。

「モダンな IaC = プログラミング言語を使うこと」ではなく、「保守可能で、チーム全体で運用できるインフラコード」こそが、真のモダンな IaC です。


参考資料

Discussion