はじめに
以前、Hyper-VへのAlmaLinuxのインストールをKickstartで自動化する記事を書いた。
その記事の最後で、
Ubuntuではautoinstallというもので、Kickstartと同様のことが出来るようだ。いずれ、試してみたいと思う。
と書いたので、今回は実際に試してみる。
Ubuntu Serverでは、SubiquityというインストーラーからAutoinstallを利用することで、ユーザー作成やディスク設定、SSHサーバのインストール等を自動化できる。
今回は、Hyper-VのVM作成からUbuntu Serverのインストールまでを自動化してみる。
実施環境
- ホストOS:Windows 11
- ゲストOS:Ubuntu Server 26.04 LTS
- PowerShellバージョン:5.1
前提
この記事では、UbuntuのISOを加工するためにWSLを使用する。
WSLをインストールしていない場合は、事前にインストールしておく。
今回はUbuntu ServerのISOファイルをダウンロードし、以下に配置したものとする。
%USERPROFILE%\Downloads\ubuntu-26.04-live-server-amd64.iso
ISOは公式からダウンロードできる。
Autoinstallについて
Ubuntu Serverでは、Autoinstallを使ってOSインストールを自動化できる。
Autoinstallの設定はYAML形式で記述する。
設定をインストーラーへ渡す方法はいくつかあり、cloud-initのuser-dataとして渡す方法のほか、インストールメディアのルートにautoinstall.yamlを配置する方法もある。
今回は後者の方法を使う。
ISO内を以下のような構成にする。
/
├─ autoinstall.yaml
├─ boot/
│ └─ grub/
│ └─ grub.cfg
├─ casper/
└─ ...
Autoinstallの設定ファイル作成
PowerShellからAutoinstall関連ファイルを置くディレクトリを作成する。
# VMの名前
$vmName = "Ubuntu26-04-sandbox"
# VM関連ファイルの保存先
$vmPath = "$env:USERPROFILE\Hyper-V\$vmName"
# Autoinstall関連ファイルの保存先
$autoinstallDir = "$env:USERPROFILE\Hyper-V\autoinstall\Ubuntu26-04"
# autoinstall.yaml
$autoinstallYamlFile = "$autoinstallDir\autoinstall.yaml"
New-Item `
-ItemType Directory `
-Force `
-Path $autoinstallDir | Out-Null
Ubuntuのidentity.passwordには平文ではなく、暗号化されたパスワードを指定する必要がある。
今回はWSL上のopensslを使って作成する。
PowerShellから以下を実行し、WSL上に、この後使用するxorrisoとopensslをインストールしておく。
wsl bash -lc "sudo apt update && sudo apt install -y xorriso openssl"
前回と同様、ログインユーザーのパスワードをexample_user_passwordとする。
$passwordHash = (
wsl bash -lc "openssl passwd -6 'example_user_password'"
).Trim()
生成したハッシュを使って、autoinstall.yamlを作成する。
$autoinstallYamlContent = @"
autoinstall:
version: 1
# 言語
locale: en_US.UTF-8
# キーボード
keyboard:
layout: us
# タイムゾーン
timezone: Asia/Tokyo
# Ubuntu Serverのminimal構成を使用する
source:
id: ubuntu-server-minimal
# ユーザー、ホスト名
identity:
hostname: ubuntu
username: example_user
password: '$passwordHash'
# SSHサーバをインストールし、パスワード認証を許可する
ssh:
install-server: true
allow-pw: true
# LVMを使って自動的にパーティションを作成する
storage:
layout:
name: lvm
# インストール完了後に電源を切る
shutdown: poweroff
"@
# BOMなしUTF-8で保存する
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText(
$autoinstallYamlFile,
$autoinstallYamlContent,
$utf8NoBom
)
今回は以下の設定になる。
| 項目 | 設定 |
|---|---|
| ホスト名 | ubuntu |
| ユーザー名 | example_user |
| パスワード | example_user_password |
| キーボード | US |
| タイムゾーン | Asia/Tokyo |
| ディスク | LVMで自動構成 |
| SSH | 有効 |
| インストール完了後 | 電源OFF |
cloud-initのuser-dataとして渡すわけではないため、#cloud-configは付けていない。
これで、以下のファイルが作成される。
%USERPROFILE%\Hyper-V\autoinstall\Ubuntu26-04
└─ autoinstall.yaml
Autoinstall用ISOを作成する
次に、Ubuntu ServerのISOをAutoinstall用に加工する。
今回はxorrisoを使って元ISOのブート情報を引き継ぎながら、
grub.cfgへautoinstallを追加する- ISO直下へ
autoinstall.yamlを追加する
という変更を加える。
変更するファイル自体は小さいが、最終的にはUbuntu Server ISOとほぼ同じサイズのISOを書き出すため、ISOの作成にはある程度時間がかかる。
そのため、作成したgrub.cfg、autoinstall.yaml、Autoinstall用ISOは削除せず、VMを作り直す場合も使い回す。
%USERPROFILE%\Hyper-V\autoinstall\Ubuntu26-04
├─ grub.cfg
├─ autoinstall.yaml
└─ ubuntu-26.04-autoinstall.iso
設定を変更しない限り、Autoinstall用ISOを再作成する必要はない。
パスを設定する
ISOファイル等のパスを指定する。
# Ubuntu Serverの元ISO
$isoPath = "$env:USERPROFILE\Downloads\ubuntu-26.04-live-server-amd64.iso"
# 作成するAutoinstall用ISO
$autoinstallIsoPath = "$autoinstallDir\ubuntu-26.04-autoinstall.iso"
# ISOから取り出したgrub.cfgの保存先
$grubCfgPath = "$autoinstallDir\grub.cfg"
WSLからWindows上のファイルを参照できるように、Windows形式のパスをWSL形式(/mnt/c/...のような形式)へ変換する関数を作る。
function Convert-ToWslPath([string]$path) {
$result = wsl.exe --exec wslpath -a -u $path
return $result.Trim()
}
各ファイルのパスを変換する。
$isoPathWsl = Convert-ToWslPath $isoPath
$autoinstallIsoPathWsl = Convert-ToWslPath $autoinstallIsoPath
$autoinstallYamlFileWsl = Convert-ToWslPath $autoinstallYamlFile
$grubCfgPathWsl = Convert-ToWslPath $grubCfgPath
grub.cfgを取り出す
UbuntuのISOからgrub.cfgを取り出す。
$extractGrubCommand = @"
xorriso \
-osirrox on \
-overwrite on \
-indev '$isoPathWsl' \
-extract /boot/grub/grub.cfg '$grubCfgPathWsl'
"@
wsl bash -lc $extractGrubCommand
$grubCfgFile = Get-Item -LiteralPath $grubCfgPath
# grub.cfgの読み取り専用属性を解除する。
if ($grubCfgFile.IsReadOnly) {
$grubCfgFile.IsReadOnly = $false
}
grub.cfgへautoinstallを追加する
取り出したgrub.cfgのUbuntu起動時のカーネルパラメータへ、
autoinstall
を追加する。
$grubContent = [System.IO.File]::ReadAllText($grubCfgPath)
$grubContent = $grubContent -replace `
'(?m)^(\s*linux\s+/casper/vmlinuz.*?)\s+---\s*$', `
'$1 autoinstall ---'
[System.IO.File]::WriteAllText(
$grubCfgPath,
$grubContent,
$utf8NoBom
)
変更後のgrub.cfgを確認する。
Get-Content $grubCfgPath
以下のような行になっていればOK。
linux /casper/vmlinuz ... autoinstall ---
ISOを作成する
変更したgrub.cfgとautoinstall.yamlを元のUbuntu ISOへ追加し、新しいISOを作る。
$buildIsoCommand = @"
xorriso \
-indev '$isoPathWsl' \
-outdev '$autoinstallIsoPathWsl' \
-overwrite on \
-map '$grubCfgPathWsl' /boot/grub/grub.cfg \
-map '$autoinstallYamlFileWsl' /autoinstall.yaml \
-boot_image any replay \
-commit
"@
wsl bash -lc $buildIsoCommand
以下のファイルができていればOK。
%USERPROFILE%\Hyper-V\autoinstall\Ubuntu26-04\ubuntu-26.04-autoinstall.iso
当然だが、元のUbuntu公式ISOを変更しているため、作成したISOのSHA256はUbuntu公式の値とは一致しない。
元ISOのチェックサムを確認する場合は、加工前に行う。
作成したISOを確認する
VMを起動する前に、作成したISOへautoinstall.yamlが入っていることを確認する。
$checkAutoinstallCommand = @"
xorriso \
-indev '$autoinstallIsoPathWsl' \
-ls /autoinstall.yaml
"@
wsl bash -lc $checkAutoinstallCommand
autoinstall.yamlが表示されればOK。
さらに、最終的なISOのgrub.cfgも確認する。
$verifyGrubCfgPath = "$autoinstallDir\grub.final.cfg"
$verifyGrubCfgPathWsl = Convert-ToWslPath $verifyGrubCfgPath
$verifyGrubCommand = @"
xorriso \
-osirrox on \
-overwrite on \
-indev '$autoinstallIsoPathWsl' \
-extract /boot/grub/grub.cfg '$verifyGrubCfgPathWsl'
"@
wsl bash -lc $verifyGrubCommand
Get-Content $verifyGrubCfgPath
以下が入っていることを確認する。
linux /casper/vmlinuz ... autoinstall ---
Autoinstallを使ってVMを作成する
作成したISOを使ってHyper-VのVMを作る。
設定は以前のAlmaLinuxの記事とほぼ同じにした。
# Ubuntu VMのVHDX
$vhdPath = "$vmPath\$vmName.vhdx"
# 接続する仮想スイッチ
$switchName = "Default Switch"
# 動的メモリ
$dynamicMemoryEnabled = $true
# 起動時メモリ
$memoryStartupBytes = 4GB
# 最小メモリ
$minimumBytes = 2GB
# 最大メモリ
$maximumBytes = 8GB
# 仮想ディスク
$vhdSizeBytes = 40GB
# CPU
$processorCount = 2
# 第2世代VM
$generation = 2
VMを作成する。
New-VM `
-Name $vmName `
-Path $vmPath `
-NewVHDPath $vhdPath `
-SwitchName $switchName `
-NewVHDSizeBytes $vhdSizeBytes `
-Generation $generation
CPUを設定する。
Set-VMProcessor `
-VMName $vmName `
-Count $processorCount
メモリを設定する。
Set-VMMemory `
-VMName $vmName `
-DynamicMemoryEnabled $dynamicMemoryEnabled `
-StartupBytes $memoryStartupBytes `
-MinimumBytes $minimumBytes `
-MaximumBytes $maximumBytes
検証用VMなので、今回はチェックポイントを無効にする。
Set-VM `
-Name $vmName `
-CheckpointType Disabled
作成したAutoinstall用ISOをDVDドライブへ接続する。
Add-VMDvdDrive `
-VMName $vmName `
-Path $autoinstallIsoPath
Ubuntuを第2世代Hyper-V VMで起動するため、Secure Bootを有効にする。
Set-VMFirmware `
-VMName $vmName `
-EnableSecureBoot On `
-SecureBootTemplate MicrosoftUEFICertificateAuthority
ISOから起動するようにする。
$dvdDrive = Get-VMDvdDrive -VMName $vmName |
Where-Object { $_.Path -eq $autoinstallIsoPath }
Set-VMFirmware `
-VMName $vmName `
-FirstBootDevice $dvdDrive
これで準備完了。
VMを起動する。
Start-VM -Name $vmName
あとは何も操作する必要はない。
ISO直下のautoinstall.yamlがSubiquityに読み込まれ、
- ディスク設定
- Ubuntuのインストール
- ユーザー作成
- SSHサーバのインストール
等が自動的に行われる。
autoinstall.yamlで、
shutdown: poweroff
を指定したので、インストール完了後にVMの電源が切れる。
VMが停止したら、インストール用ISOを外す。
Get-VMDvdDrive -VMName $vmName |
Set-VMDvdDrive -Path $null
本体VHDXから起動するよう変更する。
$osDisk = Get-VMHardDiskDrive -VMName $vmName |
Where-Object { $_.Path -eq $vhdPath }
Set-VMFirmware `
-VMName $vmName `
-FirstBootDevice $osDisk
Ubuntuを起動する。
Start-VM -Name $vmName
Hyper-Vの接続画面も開いておく。
vmconnect.exe localhost $vmName
ログイン画面が表示されたら、
ユーザー名:example_user
パスワード:example_user_password
でログインする。
ログインできれば、自動インストール完了。
VM作成から接続までのスクリプト
これまでの処理をまとめると以下になる。
grub.cfg、autoinstall.yaml、Autoinstall用ISOは共通ディレクトリへ保存する。
すでにAutoinstall用ISOが存在する場合はISO作成処理をスキップし、既存のISOを使ってVMだけを作成する。
また、ISOを作り直す場合でも、既存のautoinstall.yamlとgrub.cfgがあれば使い回す。
$vmName = "Ubuntu26-04-sandbox"
$vmPath = "$env:USERPROFILE\Hyper-V\$vmName"
# Autoinstall関連ファイルはVMとは別の共通ディレクトリへ保存する
$autoinstallDir = "$env:USERPROFILE\Hyper-V\autoinstall\Ubuntu26-04"
$autoinstallYamlFile = "$autoinstallDir\autoinstall.yaml"
$grubCfgPath = "$autoinstallDir\grub.cfg"
$isoPath = "$env:USERPROFILE\Downloads\ubuntu-26.04-live-server-amd64.iso"
$autoinstallIsoPath = "$autoinstallDir\ubuntu-26.04-autoinstall.iso"
New-Item `
-ItemType Directory `
-Force `
-Path $autoinstallDir | Out-Null
# Autoinstall用ISOがすでに存在するか確認する
$autoinstallIsoExists = Test-Path -LiteralPath $autoinstallIsoPath
# 既存ISOがある場合
if ($autoinstallIsoExists) {
Write-Host "既存のAutoinstall用ISOを使用します: $autoinstallIsoPath"
}
# Autoinstall用ISOがない場合だけ作成する
if (-not $autoinstallIsoExists) {
# WSLに必要なパッケージを入れる
wsl bash -lc "sudo apt update && sudo apt install -y xorriso openssl"
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
# autoinstall.yamlがない場合だけ作成する
if (-not (Test-Path -LiteralPath $autoinstallYamlFile)) {
$passwordHash = (
wsl bash -lc "openssl passwd -6 'example_user_password'"
).Trim()
$autoinstallYamlContent = @"
autoinstall:
version: 1
locale: en_US.UTF-8
keyboard:
layout: us
timezone: Asia/Tokyo
source:
id: ubuntu-server-minimal
identity:
hostname: ubuntu
username: example_user
password: '$passwordHash'
ssh:
install-server: true
allow-pw: true
storage:
layout:
name: lvm
shutdown: poweroff
"@
[System.IO.File]::WriteAllText(
$autoinstallYamlFile,
$autoinstallYamlContent,
$utf8NoBom
)
}
# WindowsパスをWSL用に変換する
function Convert-ToWslPath([string]$path) {
$result = wsl.exe --exec wslpath -a -u $path
return $result.Trim()
}
$isoPathWsl = Convert-ToWslPath $isoPath
$autoinstallIsoPathWsl = Convert-ToWslPath $autoinstallIsoPath
$autoinstallYamlFileWsl = Convert-ToWslPath $autoinstallYamlFile
$grubCfgPathWsl = Convert-ToWslPath $grubCfgPath
# grub.cfgがない場合だけ元ISOから取り出す
if (-not (Test-Path -LiteralPath $grubCfgPath)) {
$extractGrubCommand = @"
xorriso \
-osirrox on \
-overwrite on \
-indev '$isoPathWsl' \
-extract /boot/grub/grub.cfg '$grubCfgPathWsl'
"@
wsl bash -lc $extractGrubCommand
}
# grub.cfgの読み取り専用属性を解除する。
$grubCfgFile = Get-Item -LiteralPath $grubCfgPath
if ($grubCfgFile.IsReadOnly) {
$grubCfgFile.IsReadOnly = $false
}
# grub.cfgにautoinstallが入っていない場合だけ追加する
$grubContent = [System.IO.File]::ReadAllText($grubCfgPath)
if (-not $grubContent.Contains("autoinstall ---")) {
$grubContent = $grubContent -replace `
'(?m)^(\s*linux\s+/casper/vmlinuz.*?)\s+---\s*$', `
'$1 autoinstall ---'
[System.IO.File]::WriteAllText(
$grubCfgPath,
$grubContent,
$utf8NoBom
)
}
# Autoinstall用ISOを作成する
$buildIsoCommand = @"
xorriso \
-indev '$isoPathWsl' \
-outdev '$autoinstallIsoPathWsl' \
-overwrite on \
-map '$grubCfgPathWsl' /boot/grub/grub.cfg \
-map '$autoinstallYamlFileWsl' /autoinstall.yaml \
-boot_image any replay \
-commit
"@
wsl bash -lc $buildIsoCommand
Write-Host "Autoinstall用ISOを作成しました: $autoinstallIsoPath"
}
# Hyper-V VMを作成する
$vhdPath = "$vmPath\$vmName.vhdx"
$switchName = "Default Switch"
$dynamicMemoryEnabled = $true
$memoryStartupBytes = 4GB
$minimumBytes = 2GB
$maximumBytes = 8GB
$vhdSizeBytes = 40GB
$processorCount = 2
$generation = 2
New-VM `
-Name $vmName `
-Path $vmPath `
-NewVHDPath $vhdPath `
-SwitchName $switchName `
-NewVHDSizeBytes $vhdSizeBytes `
-Generation $generation
Set-VMProcessor `
-VMName $vmName `
-Count $processorCount
Set-VMMemory `
-VMName $vmName `
-DynamicMemoryEnabled $dynamicMemoryEnabled `
-StartupBytes $memoryStartupBytes `
-MinimumBytes $minimumBytes `
-MaximumBytes $maximumBytes
Set-VM `
-Name $vmName `
-CheckpointType Disabled
# Autoinstall用ISOをDVDドライブへ接続する
Add-VMDvdDrive `
-VMName $vmName `
-Path $autoinstallIsoPath
# Secure Boot
Set-VMFirmware `
-VMName $vmName `
-EnableSecureBoot On `
-SecureBootTemplate MicrosoftUEFICertificateAuthority
# ISOから起動する
$dvdDrive = Get-VMDvdDrive -VMName $vmName |
Where-Object { $_.Path -eq $autoinstallIsoPath }
Set-VMFirmware `
-VMName $vmName `
-FirstBootDevice $dvdDrive
# インストール開始
Start-VM -Name $vmName
# インストール終了まで待機する
$timeoutMinutes = 60
$deadline = (Get-Date).AddMinutes($timeoutMinutes)
while ((Get-VM -Name $vmName).State -ne "Off") {
if ((Get-Date) -gt $deadline) {
throw "インストール完了待ちがタイムアウトしました。VMの画面を確認してください。"
}
Start-Sleep -Seconds 10
}
# インストールISOを外す
Get-VMDvdDrive -VMName $vmName |
Set-VMDvdDrive -Path $null
# OSディスクから起動するよう変更する
$osDisk = Get-VMHardDiskDrive -VMName $vmName |
Where-Object { $_.Path -eq $vhdPath }
Set-VMFirmware `
-VMName $vmName `
-FirstBootDevice $osDisk
# インストール済みUbuntuを起動する
Start-VM -Name $vmName
# Hyper-V接続画面を開く
vmconnect.exe localhost $vmName
スクリプトを初めて実行したときだけAutoinstall用ISOが作成される。
2回目以降は既存のISOを使用するため、毎回数GBのISOを書き出す必要はない。
autoinstall.yamlの内容を変更した場合は、変更後の内容をISOへ反映するため、ubuntu-26.04-autoinstall.isoを削除してからスクリプトを再実行する。
VMの削除
作成したVMを削除する場合は以下。
# 作成したVMの名前
$vmName = "Ubuntu26-04-sandbox"
# VM固有の関連ファイル
$vmPath = "$env:USERPROFILE\Hyper-V\$vmName"
# VMを取得する
$vm = Get-VM `
-Name $vmName `
-ErrorAction SilentlyContinue
if ($null -ne $vm) {
# VMを停止する
if ($vm.State -ne "Off") {
Stop-VM `
-Name $vmName `
-TurnOff `
-Force
while ((Get-VM -Name $vmName).State -ne "Off") {
Start-Sleep -Seconds 1
}
}
# VMを削除する
Remove-VM `
-Name $vmName `
-Force
}
# autoinstall関連ファイル以外のファイルも削除する
if (Test-Path -LiteralPath $vmPath) {
Remove-Item `
-LiteralPath $vmPath `
-Recurse `
-Force
}
これでVMとVHDX等のVM固有ファイルだけを削除する。
Autoinstall関連ファイルは、
%USERPROFILE%\Hyper-V\autoinstall\Ubuntu26-04
へ別管理しているため、この削除処理では削除しない。
次にVMを作成するときも、同じgrub.cfg、autoinstall.yaml、Autoinstall用ISOをそのまま使える。
おわりに
以前の記事ではAlmaLinuxをKickstartで自動インストールしたが、UbuntuでもAutoinstallを使うことで同じようなことができた。
今回の方法では、
autoinstall.yamlを作成する- GRUBへ
autoinstallを追加する autoinstall.yamlを組み込んだAutoinstall用ISOを作成する
という手順で自動インストールできるようにした。
当初はcloud-initのNoCloudを使い、user-dataとmeta-dataをISOへ入れる方法も試したが、ISO直下へautoinstall.yamlを配置する方法の方が構成を単純にできた。
また、Autoinstall用ISOの作成には時間がかかるため、grub.cfg、autoinstall.yaml、Autoinstall用ISOはVMとは別の場所へ保存し、VMを削除しても残して使い回すようにした。
ちなみに、同様の手順で24.04も自動インストールできた。