Azure VM Windows10 / 11 の全自動日本語化

Azure 仮想マシン の日本語化を、可能な限り簡素な仕組みで完全対応することを目指してみました。<br>従来から公開されている方法では、ISOをマウントしたり RunOnceを使って複数回再起動をする必要があるなど スクリプトや手順が複雑化していて、内容を解析して理解し 各自でカスタマイズすることが難しい点がありました。<br>今回、提供する方法なら 誰でも内容を簡単に理解して利用する事ができると思います。

# 当スクリプトを作成することにした経緯と現状
従来より 多く知られている Azure VM の日本語化は 言語パックや FOD の導入のあと 一旦 再起動し ユーザーの初回ログオン実施後に システムロケール や ようこそ画面 の設定を行う必要があります。
おそらくは、この方法を採用しないと Microsoft から サポートされた方法にはならないのではないかと考えています。
しかし、昨夏に Install-Language というコマンドが提供されてから あまり期間が経過していないため、このコマンドを Azure VM の展開と組み合わせた事例が見受けられず、情報が少ないです。
現時点では、私が この記事で紹介している スクリプト は、Azure VM の展開時のみ 正常に動作しており、VM展開後に Windows PowerShell コンソール から実行すると 再起動後に RDP接続できない不具合に見舞われます。
これは、このスクリプトが デプロイ時に組み込まれて動作することで、従来の問題をクリアできているのではないかと考えています。

# この仕組みのメリット
- 15行程度の短いスクリプトで構成（全体像を理解しやすい）
- スクリプト内にログオンアカウントの記載が不要
- Windows10/11 共通で使用できる
- GUI操作で日本語化した時の結果と比べて 遜色ない仕上がり
- デプロイ時に Market Place で提供されているイメージを元に自由に選んでデプロイできる

# 制限事項
- OSは、Windows 10 20H2以降と、Windows 11 のみ対応（その他の OS では不可）
- 処理時間は、D2sV3 の VM で、20分程度
　※ 言語パック導入で 10分、Windows Updateで 10分 ＝ 合計20分
- スクリプトは、VMデプロイ時に 拡張機能(Custom Script Extension) を使ってインストールすることを強く推奨（手順は後述）
　※ Windows PowerShell コンソールから実行した場合 日本語化はされるものの 再起動後に RDPできなくなる事象を確認しています。
- 複数台の VM を同時にデプロイする場合、リソースグループ が同一だと Custom Script の実行時に Conflictエラーが発生します。これは、当スクリプトのせいでは無く Azure の制限のようです。これを回避するためには、リソースグループ を別のものにすることで回避することができます。

# 日本語化スクリプト
以下のスクリプトを ストレージアカウント上に BLOB として保存しておき、VMデプロイ時に 拡張機能 (Custom Script Extension) で、スクリプトを指定することで実行できます。
この手順は 後述しています。
````
# 言語パックインストール
Install-Language ja-jp

# システムロケールの設定
Set-WinSystemLocale -SystemLocale ja-JP

# タイムゾーンの設定
Set-TimeZone -Id "Tokyo Standard Time"

# 地域の設定
Set-Culture -CultureInfo ja-JP

# システム優先言語の設定
Set-SystemPreferredUILanguage ja-JP

# ロケーションの設定
Set-WinHomeLocation -GeoId 0x7a

# 言語リストの設定
$mylang = New-WinUserLanguageList -Language "ja-JP"
Set-WinUserLanguageList $mylang -Force

# ようこそ画面へ設定のコピー
Install-Language ja-jp -CopyTosettings

# Windows Update の実行
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Install-Module PSWindowsUpdate -Force
Get-WindowsUpdate -AcceptAll -Install -AutoReboot
````
# 注意事項
非常にシンプルなスクリプトですが、各コマンドの順序やパラメーターに重要な意味があります。改変すると不具合が発生します。<br>完成までに相当な検証工数を掛けてバランス調整していますので、基本的には 上記の内容で そのままご使用ください。<br>Windows Update と 再起動の部分を省いて利用すると 約10分で完了します。この場合は 初回ログオン時に完全に日本語化されていて、これで十分に見えるのですが再起動後に RDP できなくなります。本事象を Microsoftサポートに問い合わせたところ、Windows Update の実行が必須であるとのアドバイスをうけ、Windows Update の最適な処理を研究して追加し さらに検証と調整を重ねて完成に至っています。

【MicrosoftサポートBlog：言語パックインストール時のご留意点】
https://jpwinsup.github.io/blog/2022/06/22/UserInterfaceAndApps/LanguageSupport_IME/lp-consideration/

# スクリプト配置手順（初回のみ）
0. 当サイトに投稿されているスクリプトをテキストエディタへコピー＆ペーストして、ファイル (拡張子は .ps1) として 任意のフォルダに保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/ee9ad7e6-b90e-623d-33b4-0cc1810ee9f0.png)

0. ストレージアカウント を新規作成します。既に 運用中の ストレージアカウント でも構いません。
・リソースグループ：任意
・ストレージアカウント名：任意（グローバルでユニーク）<br>　※サブスク利用者で共用して使う場合、ソート順で一番上に表示されるような名称にすることをお勧めします。
・冗長性：任意<br>　※ローカル冗長ストレージ(LRS) で十分　※一番安価なコストで利用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/98f2d8ae-da9d-198a-7d6a-42e4ad24d5c0.png)
0. ストレージアカウント がデプロイ出来たら、「ストレージブラウザー」－「BLOBコンテナー」を選択して、「＋コンテナーを追加する」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/24e88f12-9f4d-f2ec-6cab-6a374b259c6b.png)
0. 判りやすい 任意 のコンテナ名を付与して、「作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/4af281c6-38f1-d461-4a3e-f3cd1c84b87f.png)
0. コンテナーが作成されたら、そのコンテナ名をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/45112253-58af-eb3f-0fcb-c1d2588621c3.png)
0. 下線のとおり、コンテナ名が表示されている事を確認して、「アップロード」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/2fb34d63-9cc5-6b83-2421-85df836ed6e5.png)
0. BLOBのアップロード画面で「ファイルの参照」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/f99b772f-5e1c-7840-80dd-69a6aeb92b2c.png)
0. ファイル選択ダイアログで、あらかじめ保存しておいた スクリプトを選択して「開く」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/c3c98317-85a5-91b3-1594-bfbf633403a5.png)
0. 下線部の通りに ファイルが選択されている事を確認して「アップロード」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/75938541-88e0-78a6-2856-57aaadfa335a.png)
0. アップロードに成功すると、下線部の通りに 表示される。これで 事前準備は OK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/fa3ca032-bd24-5a44-7630-ff6ad3bcdf6c.png)

# VMデプロイ手順
0. 「基本」タブで 各種設定を行ったあと、「詳細」タブを押し、「インストールする拡張機能を選択します」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/502d5e62-f431-0322-9316-e254ee75ac32.png)
0. Custom Script Extension を選択して、「次へ」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/972127e8-9092-f7d9-9dd2-d10202d7cf5d.png)
0. 以下の画面で、「参照」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/03f08b2c-a781-6120-bb3e-a463952a5284.png)
0. あらかじめ用意しておいたスクリプトが配置された ストレージアカウント と コンテナ を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/09da0681-cc1c-4da9-53ab-5dbbf48fd434.png)
0. 用意しておいた スクリプト を選んで、「選択」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/e9d3b4dd-7e32-735c-03c7-e20006fc9736.png)
0. 下線部の通りに スクリプト名 が表示されるため、「作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/42c13a57-e509-cbdb-e2ea-01e601950da7.png)
0. 下線部の通り 拡張機能欄に「Custom Script Extension」が表示されるので、「確認および作成」をクリックする事で VM のデプロイが開始されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/de03a56f-c236-15d7-dcf5-7a408941bada.png)
★デプロイ開始前のパラメータチェックの際に、設定が正しくても、以下のように　検証で失敗することがあるようです。これは Azure  Portal の不具合らしく、「ネットワーク」タブを表示させたあと、再度「確認および作成」を押せば 問題なく先へ進めます。私のスクリプトのせいではありませんので、ご注意ください。
※Custom Script Extension を選択すると出やすいのかな？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/faebcd8e-6d41-a720-3b8f-9e423a556f6c.png)
0. デプロイが始まると、以下の画面になりますが 下線部の表示中に 日本語化 が行われています。ここで 20分程度 待つと処理が終了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/0c18cb18-28db-9151-4267-e44a4f36ad4e.png)

# 初回サインイン
下図のように、初めから 日本語化 された状態で サインイン ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/5935df09-a8c1-7575-1faf-3f6940db4807.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/1fb23227-af98-220b-c24a-7d004547abd0.png)

# 参考にしたサイト
【言語パックの管理に便利な PowerShell コマンドレットのご紹介】
https://jpwinsup.github.io/blog/2023/03/06/UserInterfaceAndApps/LanguageSupport_IME/InstallLanguage/
→　このサイトを発見したことが この取り組みを実施する きっかけ になりました。しかしながら このコマンドだけだと VMデプロイ時に組み込むと RDP できなくなったり 不具合に見舞われてしまい、成功パターンを作り上げるために こんなに苦労するとは思っていませんでした。
※ブログ記事には、下記の「ご留意点」を実施しなくても OK と書かれてはいるのですけどね。 

【MicrosoftサポートBlog：言語パックインストール時のご留意点】
https://jpwinsup.github.io/blog/2022/06/22/UserInterfaceAndApps/LanguageSupport_IME/lp-consideration/
→　Microsoftサポート に問合せして、上記の記事のアドバイスを受けられなかったら 完成することはできず、ずっとハマったままだったかもしれません。大変参考になりました。

【PowerShell】Windows Updteを自動化
https://boonx4m312s.hatenablog.com/entry/2023/02/02/180000
→　Windows Update を コマンドで全自動化する方法は、ネットで調べると いくつかの方法がみつかるのですが、VM デプロイ時に組み込もうとすると、うまく動作させられず なかなか良い方法がありませんでした。そんな中で この記事で紹介されていた NuGetプロバイダー インストール時の一時中断を回避する方法が決め手となり、採用に至りました。
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
なお、バージョン(2.8.5.201) を固定で指定している箇所は、今後 永続的に使えるかどうかが定かではなく ちょっと気持ち悪い気がしており 改変の余地があると考えています。もっと良い方法をご存じの方がいたら、コメント欄で教えてください。

【Azure コラム 第8回：Windows VM 全自動日本語化】
https://www.intellilink.co.jp/column/ms/2022/020800.aspx
→　弊社の原田さんの記事です。初めは この記事をベースに 日本語化 を試行していたのですが、MSサポートブログで 2022年8月から Install-languageコマンドが提供されていたことを知ってから、もっとシンプル化できないか？ という思いが生じたため カスタマイズを繰り返して 完成形までこぎつけました。システムロケールや地域、言語バーなどの設定は このサイトを参考にしながら コマンドリファレンスや その他の日本語化のサイトなどを比較検討して組み立てを行っています。

【Windows でのカスタムのスクリプト拡張機能】
https://learn.microsoft.com/ja-jp/azure/virtual-machines/extensions/custom-script-windows#tips
→　このサイトに、カスタムスクリプト内で「再起動を行わないように」との注意書きの記載があり、スクリプトの検討時に参考になりました。

【私の記事：手動で Azure VM の日本語化】
https://qiita.com/carol0226/items/8cc48bb262bf81dfc9df
→　スクリプトではなく、GUI操作 で日本語化する場合は、この手順になります。基本的には この GUI操作 と同じようなことを、本記事では スクリプトで実装しています。

# 動作確認
## 検証済みイメージ
- Windows 11 Pro 22H2 Gen2 x 5回
- Windows 11 Pro 21H2 Gen2 x 2回
- Windows 10 Pro 22H2 Gen2 x 10回以上
- Windows 10 Pro 22H2 Gen1 x 2回
- Windows 10 Pro 21H2 Gen2 x 2回
- Windows 10 Pro 21H2 Gen1 x 2回

※Windows 10 Pro 20H2 以降であれば 仕様上は動作するはず。
　それより古い（2004以前）バージョンは、コマンドの互換性が無くて動作しません。
　Windows Server OS もコマンドの互換性が無くて動作しません。

## テスト項目
- デプロイ後に 管理者ユーザーで RDP 接続
- 「設定」－「時刻と言語」を表示して 以下の項目が正しく設定されていることを確認
　・日付と時刻
　・地域
　・言語
　・管理用の言語の設定（ようこそ画面と新しいユーザーアカウント、システムロケール）
- 再起動後に 再度 RDP接続
- 新しいローカルユーザーを追加して そのユーザーでサインイン
- 同様に「設定」－「時刻と言語」を表示して 以下の項目が正しく設定されていることを確認
　・日付と時刻
　・地域
　・言語
　・管理用の言語の設定（ようこそ画面と新しいユーザーアカウント、システムロケール）
- 並行デプロイ
　・リソースグループを別にすることで 10台のVMを並行デプロイして動作可 x 1回
　　※リソースグループが同一だと Conflictが発生して 2台目以降は 並行ではデプロイできません。