「複数台を同じ構成にしたいのに、毎回同じコマンドを打つのが面倒だ」
VMを1台作るたびに初期設定を手でやっていると、台数が増えるにつれて設定漏れや設定ミスが増えてきます。AzureのLinux VMには、OS起動時に自動で初期設定を流し込める仕組みが標準で備わっています。それが cloud-init です。
この記事では、az vm createの --custom-data フラグを使ってcloud-init YAMLをAzure VMに渡し、起動と同時にパッケージインストール・ユーザー作成・ファイル配置を自動化する実践手順を解説します。Azure CLI 2.61以上・AlmaLinux 9.4で動作確認済みです。
この記事のポイント
・cloud-initはAzure VMのOS起動時に自動実行される初期設定ツールで--custom-dataで渡せる
・YAMLの冒頭に #cloud-config を必ず書かないとcloud-initとして認識されない
・実行ログは /var/log/cloud-init.log と /var/log/cloud-init-output.log で確認できる
・複数VMを同じ構成で量産する場合はcloud-init YAMLをファイル化してazコマンドに渡す
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
cloud-initとは何か(AzureでのVM自動初期設定の仕組み)
cloud-initはLinuxカーネル起動後に実行される初期設定ツールで、AWS・Azure・GCPといった主要クラウドのLinux VMに標準で組み込まれています。VMの初回起動時にクラウドのメタデータAPIに問い合わせ、ユーザーが渡したYAMLに従ってパッケージインストールやファイル配置、任意コマンドの実行を自動で行います。AzureでのVM作成フローに当てはめると、次の順序で処理が流れます。
・az vm create --custom-data:cloud-init YAMLをBase64エンコードしてVMのカスタムデータとして渡す
・OS起動:Linux VM内でcloud-initデーモンが起動する
・cloud-init実行:メタデータAPIからYAMLを取得し、定義された設定を順番に処理する
・設定完了:ユーザーがSSH接続する時点ですでに初期設定が終わっている
手動設定との最大の違いは、「VMが使えるようになった瞬間に初期設定が完了している」点です。Ansibleやシェルスクリプトによるプロビジョニングは「VM起動後に別途実行が必要」ですが、cloud-initはVM作成フロー自体に初期設定を組み込めます。
cloud-initが扱えるディレクティブ(命令)の主要なものを整理しておきます。
・packages:インストールするパッケージの一覧
・users:OSユーザーの作成とSSH公開鍵の登録
・write_files:特定のパスにファイルを配置する
・runcmd:任意のコマンドを起動後に実行する
・timezone:タイムゾーンの設定(例:Asia/Tokyo)
・package_update:インストール前にyum/apt updateを走らせるかどうか
実行環境と前提条件
本記事のコマンドは以下の環境で動作確認しています。・Azure CLI:2.61.0(az --version で確認)
・実行環境:Ubuntu 24.04 LTS(ローカルPC)
・作成するVM OS:AlmaLinux 9.4(イメージ名:almalinux:almalinux:9-gen2:latest)
・サブスクリプション:有効なAzureサブスクリプションが必要
作業前にAzure CLIがインストールされていることと、az loginで認証済みであることを確認してください。
# Azure CLIのバージョン確認 az --version # ログイン状態の確認(アカウントが表示されれば認証済み) az account show --output table
cloud-init YAMLの基本(#cloud-config構文の書き方)
cloud-init YAMLを書く際に最初に覚えるべきルールが1つあります。ファイルの1行目に必ず#cloud-config を書くことです。これがないとAzureはファイルをcloud-initの設定として認識せず、ただのファイルとして扱います。#cloud-config # ↑ 1行目は必ずこの行。コメントではなく識別子として機能する
1. パッケージのインストール(packages)
packages ディレクティブに一覧を書くと、OS起動後にパッケージマネージャー(AlmaLinux/RHELではdnf)が自動でインストールします。#cloud-config # パッケージリポジトリを更新してからインストール package_update: true package_upgrade: false packages: - httpd - firewalld - git - vim-enhanced
package_update: true を指定するとインストール前にリポジトリキャッシュを更新します。package_upgrade: true にするとシステム全体のアップデートも実行します(初回セットアップで最新化する場合は有効ですが、起動時間が延びる点に注意してください)。2. ユーザー作成とSSH鍵の配布(users)
users ディレクティブでOSユーザーを作成し、SSH公開鍵を同時に登録できます。チームメンバー全員の公開鍵を複数台のVMに一括配布するときに特に便利です。#cloud-config users: - default # デフォルトユーザー(azureuserなど)は維持する - name: devops groups: wheel sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx admin@example.com - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIyyyy...yyyyyyyyyy dev01@example.com
default エントリを残すことで、Azureが作成するデフォルトユーザー(azureuser)を消さずに済みます。default を書かないと既存のデフォルトユーザーが消えてしまい、いざという時にSSHで入れなくなるため注意が必要です。ssh_authorized_keys には公開鍵を複数行で並べられます。これを使えば ~/.ssh/authorized_keys に個別に追加する作業が不要になります。3. 起動後コマンドの実行(runcmd)
runcmd ではOS起動後に任意のコマンドをリスト順に実行できます。サービスの有効化やファイアウォールの設定など、パッケージインストール後に必要な追加作業をまとめて記述できます。#cloud-config packages: - httpd - firewalld runcmd: # タイムゾーンをJSTに設定 - timedatectl set-timezone Asia/Tokyo # firewalldを有効化してhttpを開放 - systemctl enable firewalld - systemctl start firewalld - firewall-cmd --permanent --add-service=http - firewall-cmd --reload # httpdを有効化して起動 - systemctl enable httpd - systemctl start httpd
root 権限で実行されます。sudoは不要です。コマンドの実行順序はリストの上から下への順番が保証されています。4. ファイルの配置(write_files)
write_files ディレクティブで、特定のパスに設定ファイルを配置できます。content に直接ファイル内容を書き込む方式で、外部ファイルの転送が不要です。#cloud-config write_files: - path: /var/www/html/index.html owner: apache:apache permissions: "0644" content: |
Azure VM - cloud-init setup complete
- path: /etc/motd content: | =================================================== This server was provisioned by cloud-init. Unauthorized access is prohibited. ===================================================
permissions は文字列として "0644" のように引用符付きで書かないと、YAMLが数値として解釈してパーミッションの指定が狂うことがあります。必ず引用符を付けてください。AzureのVM作成時にcloud-initを組み込む手順
1. cloud-init YAMLをファイルとして保存する
ここまでのディレクティブをまとめた実践的なYAMLを作成します。Webサーバー(httpd)のセットアップを自動化する例です。# ファイル名: cloud-init-webserver.yaml #cloud-config package_update: true packages: - httpd - firewalld - vim-enhanced users: - default - name: webadmin groups: wheel sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIxxxxxxxxxxxxxxxxxxxx admin@example.com write_files: - path: /var/www/html/index.html owner: apache:apache permissions: "0644" content: |
Provisioned by cloud-init on Azure
runcmd: - timedatectl set-timezone Asia/Tokyo - systemctl enable firewalld - systemctl start firewalld - firewall-cmd --permanent --add-service=http - firewall-cmd --permanent --add-service=https - firewall-cmd --reload - systemctl enable httpd - systemctl start httpd
2. az vm createで--custom-dataを指定する
--custom-data にYAMLファイルのパスを渡します。Azureは自動でBase64エンコードしてVMのカスタムデータ領域に保存します。# リソースグループを作成 az group create \ --name myWebRG \ --location japaneast # cloud-init付きのLinux VMを作成 az vm create \ --resource-group myWebRG \ --name myWebVM \ --image almalinux:almalinux:9-gen2:latest \ --size Standard_B1s \ --admin-username azureuser \ --generate-ssh-keys \ --custom-data @cloud-init-webserver.yaml \ --public-ip-sku Standard \ --output table # 実行結果(publicIpAddressの値を控えておく) ResourceGroup PowerState PublicIpAddress Fqdns PrivateIpAddress --------------- ------------ ----------------- ------- ------------------ myWebRG VM running 20.xx.xx.xxx 10.0.0.4
@cloud-init-webserver.yaml の @ はAzure CLIの「ファイルから読み込む」指定です。パスの先頭に付けることでYAMLファイルの内容がそのまま渡されます。VMの作成完了後、cloud-initがバックグラウンドで処理を続けています。httpdが起動するまで30秒~3分程度かかります。すぐにSSHで入っても
systemctl status httpd が inactive になっている場合は、もう少し待ってから確認してください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Azureの学習をもっと体系的に進めたいなら、Azure実践ハンズオン講座をご覧ください。現役エンジニアが教える実務直結カリキュラムで、cloud-initによる自動化からインフラ管理まで一気に習得できます。
3. cloud-initの実行ログを確認する
VMにSSHで接続してcloud-initの実行ログを確認します。# VMにSSH接続 ssh azureuser@20.xx.xx.xxx # cloud-initの全体ステータスを確認 [azureuser@myWebVM ~]$ cloud-init status status: done # cloud-initの詳細ログ(実行されたモジュールと結果を確認) [azureuser@myWebVM ~]$ sudo cat /var/log/cloud-init.log | tail -30 2026-07-24 10:23:05,481 - util.py[DEBUG]: Running command ['timedatectl', 'set-timezone', 'Asia/Tokyo'] ... 2026-07-24 10:23:05,498 - util.py[DEBUG]: Running command ['systemctl', 'enable', 'firewalld'] ... 2026-07-24 10:23:06,701 - util.py[DEBUG]: Running command ['systemctl', 'start', 'firewalld'] ... 2026-07-24 10:23:07,143 - util.py[DEBUG]: Running command ['firewall-cmd', '--permanent', '--add-service=http'] ... 2026-07-24 10:23:07,512 - util.py[DEBUG]: Running command ['systemctl', 'enable', 'httpd'] ... 2026-07-24 10:23:07,793 - util.py[DEBUG]: Running command ['systemctl', 'start', 'httpd'] ... # コマンドの実際の出力を確認(インストール進捗などはこちら) [azureuser@myWebVM ~]$ sudo cat /var/log/cloud-init-output.log | tail -50
・/var/log/cloud-init.log:cloud-initが実行したモジュールと各モジュールの成否を記録するメインログ
・/var/log/cloud-init-output.log:runcmdや各コマンドの実際の標準出力・標準エラーを記録するログ
packages設定でインストールしたパッケージのdnf出力は
cloud-init-output.log 側に出るため、トラブルシュート時は両方を確認してください。# httpdの起動状態を確認 [azureuser@myWebVM ~]$ systemctl status httpd * httpd.service - The Apache HTTP Server Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled) Active: active (running) since Thu 2026-07-24 10:23:08 JST; 3min ago # webadminユーザーが作成されていることを確認 [azureuser@myWebVM ~]$ id webadmin uid=1001(webadmin) gid=1001(webadmin) groups=1001(webadmin),10(wheel) # タイムゾーンがJSTになっていることを確認 [azureuser@myWebVM ~]$ timedatectl | grep "Time zone" Time zone: Asia/Tokyo (JST, +0900)
複数VMへの量産パターン(同一構成の一括セットアップ)
cloud-initの本領は複数VMへの適用です。同じYAMLを使って同一構成のVMを複数台、コマンド一発で量産できます。# 3台のWebサーバーを同じcloud-initで量産する for i in 1 2 3; do az vm create \ --resource-group myWebRG \ --name "myWebVM-${i}" \ --image almalinux:almalinux:9-gen2:latest \ --size Standard_B1s \ --admin-username azureuser \ --generate-ssh-keys \ --custom-data @cloud-init-webserver.yaml \ --public-ip-sku Standard \ --no-wait done # 全VMの状態をまとめて確認 az vm list \ --resource-group myWebRG \ --show-details \ --query "[].{Name:name, State:powerState, IP:publicIps}" \ --output table Name State IP ---------- ------------ ----------- myWebVM-1 VM running 20.xx.xx.1 myWebVM-2 VM running 20.xx.xx.2 myWebVM-3 VM running 20.xx.xx.3
--no-wait フラグを付けると各VMの作成完了を待たずにループが進むため、3台の作成を並行して実行できます。全台が VM running になったことを az vm list で確認してから動作テストを行ってください。Azureの場合、cloud-initはVMのProvisioning段階(azure-walinuxagentとの連携)で実行されます。このため
az vm create コマンドが返ってきた時点でcloud-initの全処理が完了しているとは限りません。cloud-init status で status: done が確認できるまで設定依存のサービスを使い始めないことが鉄則です。Linux ポート確認の全コマンドで解説しているssやnetstatを使ってhttpdがポート80でリスンしているか確認するのが、動作検証の定石です。
cloud-initが動かない時のトラブルシュート
1. #cloud-config が1行目に書かれているか確認する
最も多いミスは、YAMLの1行目に空行が入っていたり、BOMが付いてしまうケースです。#cloud-config は文字通りファイルの最初の行でなければなりません。# 正しい形式(1行目が #cloud-config) #cloud-config package_update: true ... # NGな形式(空行や別のコメントが先に来ている) # 設定ファイル v1.0 ← これがあると cloud-init として認識されない #cloud-config ...
2. cloud-init status でエラーを確認する
cloud-init status で status: error が返ってきた場合は、--long オプションで詳細を確認します。# エラーが出た場合(--longで詳細確認) [azureuser@myWebVM ~]$ cloud-init status --long status: error boot_status_code: enabled-by-kernel-cmdline-generator detail: Start: Thu, 24 Jul 2026 10:23:00 +0000 Finish: Thu, 24 Jul 2026 10:24:12 +0000 Last Errors: - stage: final errors: - Command ... returned non-zero exit status # エラーの詳細はcloud-init-output.logを確認 [azureuser@myWebVM ~]$ sudo grep -A 5 "ERROR\|error" /var/log/cloud-init-output.log
3. cloud-initをデバッグ目的で手動再実行する
cloud-initは通常VMの初回起動時にのみ実行されます。YAMLを修正して再テストしたい場合は、以下の手順でリセットして再実行できます。ただし、本番VMでは実行しないこと(ユーザーの再作成やファイルの上書きが発生します)。# cloud-initの実行状態をリセット(テスト環境のみ) sudo cloud-init clean --logs # cloud-initを再実行 sudo cloud-init init sudo cloud-init modules --mode=config sudo cloud-init modules --mode=final # 再実行後にステータス確認 sudo cloud-init status
4. YAMLの構文エラーをローカルで事前チェックする
az vm createを実行する前にYAMLの構文を確認しておくと、無駄なVM作成コストを抑えられます。cloud-init自体にdevel schema --config-file コマンドが用意されています。# cloud-initのYAML構文検証(cloud-initがインストールされたLinux上で実行) sudo cloud-init devel schema --config-file cloud-init-webserver.yaml # エラーなしの場合 Valid cloud-config: cloud-init-webserver.yaml # エラーありの場合(具体的な行番号とエラー内容が表示される) Error: cloud-config.yaml failed schema validation! users.1.ssh_authorized_keys: ... is not valid ...
schema コマンドを使って事前検証できます。CI/CDパイプラインで新しいYAMLを検証してからデプロイする場合にも使える手法です。本記事のまとめ
AzureのLinux VMにcloud-initで初期設定を自動化する手順を解説しました。| やりたいこと | cloud-initの書き方 / コマンド |
|---|---|
| パッケージをインストールする | packages: [httpd, firewalld] |
| ユーザーを作成しSSH鍵を登録する | users: [{name: devops, ssh_authorized_keys: [...]}] |
| ファイルを特定のパスに配置する | write_files: [{path: /etc/motd, content: ...}] |
| 任意のコマンドを起動後に実行する | runcmd: [systemctl enable httpd, ...] |
| VMにcloud-initを渡して作成する | az vm create --custom-data @cloud-init.yaml |
| cloud-initの実行状態を確認する | cloud-init status |
| 実行ログを確認する | sudo cat /var/log/cloud-init-output.log |
| YAMLの構文エラーを事前検証する | sudo cloud-init devel schema --config-file file.yaml |
runcmd に組み込むことで、サーバー構築の自動化範囲をどんどん広げていけます。
Azureの学習をもっと体系的に進めたいなら、Azure実践ハンズオン講座をご覧ください。現役エンジニアが教える実務直結カリキュラムで、VM構築からインフラ管理まで一気に習得できます。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:AzureのRoute TableとUser Defined Routeを設定する方法|サブネットのカスタムルーティングをazコマンドで実践
- この記事の属するカテゴリ:Azureへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます