レジストリ設定展開は簡単!
ただし煩雑で、設定するレジストリキーの数に比例して煩雑さが増えてしまいます。
先日あるプロジェクトで展開準備中という 100 以上のレジストリを見て私は思いました。
これは多い!!! reg add コマンドの羅列だけで流すとエラーが出た場合にエラー発生個所を追えない気がするけど大丈夫かな? 検出規則は手打ちするつもりなのかな? 検出スクリプトとか検討できてるのかな? 自分がやるのも
嫌だな時間かかるな、ツールが欲しい!!
←サンプル作りました
本記事ではその構築補助サンプル スクリプトの使用例、そして理想と現実をまとめます。
今回作成したサンプル スクリプト諸々は特に名称決まっておらず、以降「スクリプト ツール (仮)」や、省略して「スクリプト」と呼称します。
なお、本記事で扱う範囲は以下になります:
# | カテゴリ | 本記事の範囲 | 備考 |
---|---|---|---|
1 | デバイス プラットフォーム | Windows 10/11 | Windows のレジストリに対する操作・制御を扱うため |
2 | デバイス参加方式 | Entra registered, joined, hybrid joined (いずれも Intune 登録済みであること) | Win32 アプリの前提要件1に準拠 |
3 | 展開対象設定 | HKLM2 のレジストリ設定 | CSV ファイルと PowerShell スクリプトを組み合わせて展開。 |
4 | Intune 機能 | Windows アプリ (Win32) | スクリプト単体であればスクリプト展開機能3や、修復機能も利用可。今回は CSV と汎用化した (変数を利用する) PowerShell スクリプトを組み合わせて利用するため、複数ファイルをパッケージできる Win32 アプリを利用。 |
なお、スクリプト ツール (仮) が簡略化できる範囲は、Intune Win32 アプリ実装における以下の工程です。
(全体の工程ではどこにあたるのか?については、本記事内のフローをご参照ください!)
- 実行モジュール作成 (intunewin でパッケージする中身となるレジストリ設定モジュール)
- カスタム検出スクリプト作成
本記事の内容はAs-Isであること、ご承知おきください。
本記事内のスクリプトを利用される場合は、お手元の環境で検証・評価の上でお願いいたします。
長めの記事になってしまったので、適宜目次ご活用ください!
(長いので飛ばし読み推奨です。)
1. スクリプト ツール (仮) の実態
スクリプト ツール (仮) の実態は、以下 4 つのファイルです。
# | ファイル名 | 役割 | 備考 |
---|---|---|---|
1 | RegItemList.csv | レジストリ項目のリストをスクリプトに提供する | ファイル名とヘッダー指定のあるレジストリのリスト。要件に応じて作成します。 |
2 | Set-RegProperty.ps1 | RegItemList.csv の中身に応じたレジストリを設定する | 変数を利用するため新規作成は不要。指定のパス・ファイル名の CSV ファイルをインポートし、行の数だけ New-ItemProperty を繰り返し実行する。Intune Win32 アプリのインストールコマンドとなるスクリプト。 |
3 | New-DetectionScript.ps1 | RegItemList.csv の中身に応じた検出スクリプトを生成する | 変数を利用するため新規作成は不要。指定のパス・ファイル名の CSV ファイルをインポートし、行の数だけ検出処理をアウトプットファイル (yyyyMMddHHmmss-Detection.ps1) に書き出す。 |
4 | yyyyMMddHHmmss-Detection.ps1 | カスタム検出スクリプト (デバイスのレジストリ状態が正常か異常か検出する) | New-DetectionScript.ps1 が生成するアウトプットファイル。(作業者による手動作成は不要) |
RegItemList.csv (#1) と Set-RegProperty.ps1 (#2) を同一ディレクトリ下に配置して intunewin アプリパッケージを作成し、Intune 管理センター上で新規アプリ作成時に yyyyMMddHHmmss-Detection.ps1 (#4) をカスタム検出スクリプトとして登録します。
各ファイルのサンプルは、本記事末尾に Appendix として掲載しています。
2. スクリプト ツール (仮) の使い方:Win32 アプリ 新規構築 フロー
「どこで/どのようにスクリプトを活用するのか?」が分かりやすいよう、スクリプトを使わない場合と使う場合それぞれの構築フローを以下にまとめます。
- A) スクリプトを使わない場合 (通常フロー)
- B) スクリプトを使う場合 (スクリプト活用フロー)
A) スクリプトを使わない場合 (通常フロー)
Intune から Win32 アプリとしてレジストリ設定を展開する場合、基本的に以下のステップで実装します。
ステップ | 工程 | やること |
---|---|---|
1 | 事前準備 | 設定したいレジストリ項目を、実行可能なモジュールの形式で用意します。(例:Reg ファイル、PowerShell スクリプト、etc) |
2 | 動作検証 (ローカル) | ステップ1 で用意したモジュールを実行 (検証用端末でローカル実行) し、対象レジストリが意図したとおり設定・検出できるか確認します。 |
3 | パッケージ作成 | MSのツール (IntuneWinAppUtil.exe) を実行して、.intune 形式 (Intuneから展開可能な形式) のパッケージを作成します。 |
4 | Intune Win32 アプリ作成 | ステップ3 で作成したパッケージを使い、Win32 アプリを新規作成します。この際、検出規則は GUI (手動で構成) または スクリプト (事前に別途作成が必要) を配置して設定します。 |
5 | 動作検証 (Intune アプリ展開) | ステップ4 で作成したアプリをテスト用グループに割り当て、対象レジストリが意図したとおり設定・検出できるか確認します。 |
6 | リリース | ステップ4 で作成したアプリを本番用グループに割り当てます。(以降、本番利用) |
ステップ6 のリリースは、同一環境内で事前検証 → 全体展開する、という想定で記載しました。もし検証と本番の両面ある場合は、検証環境で動作検証 → 本番環境実装する、という流れになります。
具体的手順イメージできるよう頑張って記載しましたが、ぶっちゃけこの記事のフローを追うよりもこちらの記事を参照されると一発でイメージ沸くかと思います。
- 参考: IntuneでWindows10にいろいろなものを配ってみる
←こちらの記事では、検出規則は手動で構成されています。
主観ですが、レジストリ展開するぞ!となった時、レジストリ設定用のモジュール準備に一番力が注がれ、その後の検証時間や検出規則をどう実装するのかについては考慮されず、結局 Intune 担当者が頑張って巻き取るという構図になりがちかと思います。
なんなら、「レジストリ設定用のモジュール準備から全部やってほしい。こっちはレジストリのリスト渡すから」と言われる場合もありますよね。(同意求ム)
こうした場合を見越して、モジュール準備と検出スクリプト作成をスクリプトで自動化する案が下の パターン B です。
(動作検証を削るのはリスクがありすぎるため、ここは簡略化できませんでした泣)
B) スクリプトを使う場合 (スクリプト活用フロー)
ステップ | 工程 | やること |
---|---|---|
1 | 事前準備 | 設定したいレジストリ項目をCSV形式で一覧化 (RegItemList.csv) します。 |
2 | 検出スクリプト作成 | PowerShell スクリプト (New-DetectionScript.ps1) を実行して、CSV 上のレジストリに応じた検出スクリプト (yyyyMMddHHmmss-Detection.ps1) を作成します。 |
3 | 動作検証 (ローカル) | レジストリ設定用の Set-RegProperty.ps1 → レジストリ検出用の yyyyMMddHHmmss-Detection.ps1 を順に実行 (検証用端末でローカル実行) し、対象レジストリが意図したとおり設定・検出できるか確認します。 |
4 | パッケージ作成 | MSのツール (IntuneWinAppUtil.exe) を実行して、.intune 形式 (Intuneから展開可能な形式) のパッケージ (Set-RegProperty.intunewin) を作成します。 |
5 | Intune Win32 アプリ作成 | ステップ4 で作成したパッケージ (Set-RegProperty.intunewin) と、ステップ2で作成した検出スクリプト (yyyyMMddHHmmss-Detection.ps1) を使い、Win32 アプリを新規作成します。 |
6 | 動作検証 (Intune アプリ展開) | ステップ5 で作成したアプリをテスト用グループに割り当て、対象レジストリが意図したとおり設定・検出できるか確認します。 |
7 | リリース | ステップ5 で作成したアプリを本番用グループに割り当てます。(以降、本番利用) |
非常に残念ながら、動作検証ステップは削ることができませんでした。
ただし、CSVの形式で設定したいレジストリ項目をリスト化すれば、あとはスクリプト ツールからそのCSVを参照してレジストリ設定モジュールとして機能したり、検出スクリプトを生成したりできます。
つまり、CSV さえ作れば、設定モジュールと検出スクリプトはワンタッチで作成 できます。
次項では、当環境で検証したときの動作例をベースに、各工程の作業内容を深堀します。
3. スクリプト ツール (仮) の動作
当環境で検証した際の動作をベースに、若干汎用的にスクリプト ツール (仮) の使用例・動作例をまとめます。
特に、以下にスポットライトを当てて記載します。
- ステップ1: 事前準備
- ステップ2: 検出スクリプト作成
- ステップ4: パッケージ作成
- ステップ5: Intune Win32 アプリ作成
結構長くなってしまったので、適宜目次ご活用ください。
ステップ1: 事前準備
設定対象のレジストリ項目を策定し、CSV 形式で一覧化します。
CSV であれば Intune の知識無く作成できるため、作業依頼もしやすいと思います!
CSV ファイル (RegItemList.csv) 作成
以下のファイル名とヘッダーで、CSV ファイルを作成します。
CSV のファイル名とヘッダーは、後工程で実行する PowerShell スクリプト内にベタ書きしています。
このため、もし変える場合はスクリプト内の参照部も修正ください。
- ファイル名: RegItemList.csv
- ヘッダー: No,Path,Name,Type,Value
上記ヘッダーに合わせてレジストリキー (Path)、エントリ (Name)、データ型 (Type)、値 (Value) を入力すると、実際のファイルの中身はこのようになります↓
- サンプル
RegItemList.csv サンプル
No,Path,Name,Type,Value 1,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,RestoreOnStartup,DWord,5 2,HKLM:\SOFTWARE\Policies\Microsoft\Edge,NewTabPageContentEnabled,DWord,0 3,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,HomepageLocation,String,portal.office.com 4,HKLM:\SOFTWARE\Policies\Microsoft\Edge,NewTabPageAppLauncherEnabled,DWord,0 5,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,ShowHomeButton,DWord,1 6,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,FavoritesBarEnabled,DWord,1
Edge に対する制御は、Intune の設定カタログで構成することも可能です。
(CSV 記載例として載せていますが、同様の設定を展開する要件が実プロジェクトで発生する場合は、本記事のレジストリ展開ではなく設定カタログで展開されることがおすすめです。)
「No」列は上から昇順に番号を振ります。これは管理用の数字を入れるために作りました。
(レジストリ設定・検出時にエラーが発生した場合、何番目の設定で発生したエラーか Write-Host します。)
なお、「Type」列にはレジストリのデータ型に応じて以下のいずれかの値を入れます。
(参考:New-ItemProperty - PropertyType)
Type 列で利用可能な値 | 対応するレジストリ データ型 |
---|---|
String | REG_SZ |
ExpandString | REG_EXPAND_SZ |
Binary | REG_BINARY |
DWord | REG_DWORD |
MultiString | REG_MULTI_SZ |
Qword | REG_QWORD |
Unknown | REG_RESOURCE_LISTなど、サポートされていないレジストリ データ型 |
これで CSV の用意ができました。
ステップ2: 検出スクリプト作成
PowerShell スクリプト (New-DetectionScript.ps1) を実行して、CSV 上のレジストリに応じた検出スクリプト (yyyyMMddHHmmss-Detection.ps1) を作成します。
作業環境
検出スクリプトを生成するだけの工程のため、作業者が普段業務利用しているOA端末など、Intune のアプリ構築に利用している環境上で作業すればOKです。
作業用ディレクトリ① 作成
作業用ディレクトリ① にツール配置
先ほど作成した作業用ディレクトリに、以下のファイルを配置します。
- New-DetectionScript.ps1:カスタム検出スクリプトを生成するスクリプト ツール
- RegItemList.csv:レジストリ項目のリスト
New-DetectionScript.ps1 実行
New-DetectionScript.ps1 を実行します。
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Unrestricted -Force
C:\work\Reg\New-DetectionScript.ps1
当環境では Execution Policy が既定状態なので、unrestricted にしてから実行しています。
検出スクリプト (yyyyMMddHHmmss-Detection.ps1) が出力される
New-DetectionScript.ps1 を実行した結果、同一ディレクトリ配下に検出スクリプト (yyyyMMddHHmmss-Detection.ps1) が出力されます。
ファイル名の「yyyyMMddHHmmss」部は、実行時の実際の日時になります。
本工程で作成した検出スクリプト (yyyyMMddHHmmss-Detection.ps1) は、後工程 (ステップ5: Intune Win32 アプリ作成) にて使用します。
ステップ3: 動作検証 (ローカル)
レジストリ設定用の Set-RegProperty.ps1 → レジストリ検出用の yyyyMMddHHmmss-Detection.ps1 を順に実行 (検証用端末でローカル実行) し、対象レジストリが意図したとおり設定・検出できるか確認します。
作業環境
仮想マシンなど、検証用デバイス上での動作確認推奨です。
Intune Management Extension が実行することを、事前にひとつひとつローカル実行して動作を確認する工程のため。
-
動作確認概要
- 検証用デバイスのレジストリ状態を確認、記録する
- 今回設定対象のレジストリキー/エントリの存在有無
- キー/エントリが存在する場合、値は何が設定されているか
- 検証用デバイス上に、作業用ディレクトリを用意する
- 作業用ディレクトリに以下ファイルを配置する
- RegItemList.csv
- Set-RegProperty.ps1
- yyyyMMddHHmmss-Detection.ps1
- Set-RegProperty.ps1 を実行する
- 検証用デバイスのレジストリ状態を確認、記録する
- 今回対象のレジストリは、CSV に記載の通り正しく構成されたか
-
yyyyMMddHHmmss-Detection.ps1 を実行する
- [正常パターン] CSV 通りレジストリが正しく構成されている場合: $flg = 0, かつ "All reg keys are successfully configured." と Write-Host されること
- [異常パターン] CSV 通りレジストリが構成されていない場合: $flg = 1, かつエラーが発生したCSV行項番が Write-Error されること
- 必要に応じて、レジストリ設定時のデバイス動作確認を行う
(レジストリの効力が分かる統合試験のような動作確認)
- 検証用デバイスのレジストリ状態を確認、記録する
レジストリの操作には、端末のローカル管理者権限が必要です。
(PowerShell は管理者として実行する必要があります)
ステップ4: パッケージ作成
MSのツール (IntuneWinAppUtil.exe) を実行して、.intune 形式 (Intuneから展開可能な形式) のパッケージ (Set-RegProperty.intunewin) を作成します。
作業環境
ファイルをパッケージするだけの工程のため、作業者が普段業務利用しているOA端末など、Intune のアプリ構築に利用している環境上で作業すればOKです。
作業用ディレクトリ② 作成
適当な作業用ディレクトリを用意します。
作業用ディレクトリ② にツール配置
先ほど作成した作業用ディレクトリに、以下のファイルを配置します。
- RegItemList.csv:レジストリ項目のリスト
- Set-RegProperty.ps1:レジストリ設定スクリプト
本ディレクトリ下のコンテンツがそのままパッケージングされることになるため、余計なファイルは置かず、必要なファイルのみ配置します。
作業用ディレクトリ②を intunewin パッケージ化
通常 Win32 アプリを作成するときと同様に、intunewin ファイル (パッケージ ファイル) を作成します。
ラッピングツール (IntuneWinAppUtil.exe) を実行する際、セットアップ フォルダとセットアップ ファイルとして以下を指定します。
- セットアップ フォルダ:作業用ディレクトリ② (先ほど作成し、ツールを配置したフォルダ)
- セットアップ ファイル:作業用ディレクトリ②\Set-RegProperty.intunewin
- アウトプット フォルダ:任意の場所
.\IntuneWinAppUtil.exe -c C:\work\Reg\AppPkg -s C:\work\Reg\AppPkg\Set-RegProperty.ps1 -o C:\intunewin\Reg
アウトプット フォルダとして指定した任意の場所に、intunewin ファイルが生成されます。
本工程で作成したアプリ パッケージ (Set-RegProperty.intunewin) は、次工程 (ステップ5: Intune Win32 アプリ作成) にて使用します。
ステップ5: Intune Win32 アプリ作成
ステップ4 で作成したパッケージ (Set-RegProperty.intunewin) と、ステップ2で作成した検出スクリプト (yyyyMMddHHmmss-Detection.ps1) を使い、Win32 アプリを新規作成します。
作業環境
作業者が普段業務利用しているOA端末など、Intune のアプリ構築に利用している環境上で作業すればOKです。
Intune管理センターにファイル類をアップロードしアプリを作成する作業のため。
新規アプリ作成
通常 Win32 アプリを作成するときと同様に各設定を構成していきます。
この際、以下の項目がポイントになります:
- アプリのパッケージファイル:Set-RegProperty.intunewin
- インストールコマンド:C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File Set-RegProperty.ps1
- インストールの処理:システム
- 検出規則:カスタム検出スクリプトを使用する (スクリプト:yyyyMMddHHmmss-Detection.ps1)
以下が当検証環境で利用しているパラメータ例です。
(Win32アプリとしての必須項目、およびツール利用に関連する項目に絞って記載します。その他の設定項目の利用は任意です。)
【表】当環境のパラメータ(長いので折りたたみました。左端の ▶ をクリックして展開)
# | 大項目 | 中項目 | 既定値 | 設定値 | 備考 |
---|---|---|---|---|---|
1 | アプリ情報 | ファイルの選択 | - | Set-RegProperty.intunewin | ステップ4 で作成したパッケージを選択する。 |
2 | アプリ情報 | 名前 | (実行ファイル名) | Set Edge Registry | 展開動作には関係ないので直観的に分かりやすい名前を採用。 |
3 | アプリ情報 | 発行元 | - | Sample | 構成必須の項目のため、任意の文字列を設定する。 |
4 | プログラム | インストール コマンド | - | C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File Set-RegProperty.ps1 | Win32アプリは32ビットで動くため、64ビットレジストリを操作するために sysnative 空間を利用する。PowerShell 実行のため ExecutionPolicy を設定する。パッケージ内の実行ファイルを指定する。 |
5 | プログラム | アンインストール コマンド | - | none | レジストリの書き戻し処理は用意していないため、アンインストール コマンドは存在しない。構成必須の項目のため、任意の文字列を設定する。 |
6 | プログラム | 利用可能なアンインストールを許可する | はい | いいえ | アンインストール コマンドが無いため、アンインストールボタンは表示しない。(Pull展開時) |
7 | プログラム | インストールの処理 | システム | システム | レジストリの操作には管理者権限が必要なため、システムコンテキストで実行する |
8 | プログラム | デバイスの再起動 | アプリのインストールによるデバイスの強制的な再起動を許可する | 何もしない | 今回は再起動不要。 |
9 | プログラム | リターン コード | (記載割愛) | 1; 失敗 (既定値の下に追加する) | Set-RegProperty.ps1 にてエラーが発生した場合、exit code が 1 となるため、1 = 失敗 と定義する。(追加しなくても定義に無いコードは失敗とみなされるため、本項はあくまで様式美のため構成している。) |
10 | 必要条件 | オペレーティング システムのアーキテクチャ | - | x64 | 64ビットOSのレジストリに対する設定のため。 |
11 | 必要条件 | 最低限のオペレーティング システム | - | Windows 10 22H2 | レジストリの要件に応じて設定する。 |
12 | 検出規則 | 規則の形式 | - | カスタム検出スクリプトを使用する | ステップ2 で作成したスクリプトを使用するため、形式はカスタムスクリプトを選択する。 |
13 | 検出規則 | スクリプト ファイル | - | yyyyMMddHHmmss-Detection.ps1 | ステップ2 で作成したカスタム検出スクリプトを選択する。 |
14 | 検出規則 | 64 ビット クライアントで 32 ビット プロセスとしてスクリプトを実行する | いいえ | いいえ | 64 ビット プロセスで実行する (32 ビット プロセスとしては実行しない)。 |
15 | 検出規則 | スクリプトの署名の確認を強制し、スクリプトをサイレント実行する | いいえ | いいえ | 残念ながら署名をしていないスクリプトのため、本機能は利用できない。 |
依存関係や置換は、今回の内容だと不要のため空欄のまま [次へ] 進めます。
割り当ては次ステップにて設定するため、こちらも空のまま一旦アプリを作成します。
ステップ6: 動作検証 (Intune アプリ展開)
ステップ5 で作成したアプリ (Set Edge Registry) をテスト用グループに割り当て、対象レジストリが意図したとおり設定・検出できるか確認します。
テストグループへの割り当て
通常 Win32 アプリを展開するときと同様に、Intune 管理用センターにて先ほどのアプリ (Set Edge Registry) を編集し、割り当て設定を更新します。
グループは、テスト用の端末が所属する任意のセキュリティ グループを使用します。
(ちょうどいいグループが無い場合は、まず作成してから割り当てを行う。)
作業環境
Intune管理センターのアプリ設定を編集するだけの工程のため、作業者が普段業務利用しているOA端末など、Intune のアプリ構築に利用している環境上で作業すればOKです。
アンインストール以外の、必須 (Required) または 登録済みデバイスで使用可能 (Available) いずれかの種類で割り当てます。
テストのため、通知はすべてON (既定の状態) にします。
動作確認
先ほどアプリを割り当てたグループのメンバーデバイスにて、動作を確認します。
作業環境
仮想マシンなど、検証用デバイス上での動作確認推奨です。
Intune Management Extension が実行することを、事前にひとつひとつローカル実行して動作を確認する工程のため。
また、ローカル動作確認に使った端末とは別端末を用意、またはローカル実行後の端末環境を切り戻してから動作確認するのがおすすめです。
-
動作確認概要
- 検証用デバイスのレジストリ状態を確認、記録する
- 今回設定対象のレジストリキー/エントリの存在有無
- キー/エントリが存在する場合、値は何が設定されているか
- アプリを展開する
- Required 割り当ての場合:自動的にインストールされるのを待つ
- Available 割り当ての場合:ポータル サイトからインストールする
(参考:Install and share apps on your device)
- デスクトップ通知を注視する
- インストール完了後、検証用デバイスのレジストリ状態を確認、記録する
- 通知内容 (成功/失敗) と実際のレジストリ状態は合致するか
- 必要に応じて、IntuneManagementExtension ログを確認する
- 必要に応じて、レジストリ設定時のデバイス動作確認を行う
(レジストリの効力が分かる統合試験のような動作確認) - Intune 管理センターにて、アプリのインストール結果レポートを確認する
- 検証用デバイスのレジストリ状態を確認、記録する
「必須 (Required)」割り当て利用の場合、デバイス上で Intune Management Extension エージェントが 1 時間周期でアプリの割り当て有無を確認・インストール処理を実行します。
(参考:Win32 app management in Microsoft Intune)
検証で 1 時間周期を待っていられない場合は、デバイスを再起動または「Microsoft Intune Management Extension」サービスを再起動するとアプリポールをトリガーすることができます。
ご参考:検証時のデバイス挙動
当環境で検証時、以下の動作となりました。
BEFORE
アプリ展開
AFTER
-
挙動
Edge に対する制御は、Intune の設定カタログで構成することも可能です。
(同様の設定を展開する要件が実プロジェクトで発生する場合は、本記事のレジストリ展開ではなく設定カタログで展開されることがおすすめです。)
ステップ7: リリース
アプリを本番用グループに割り当てます。(以降、本番利用)
作業環境
Intune管理センターのアプリ設定を編集するだけの工程のため、作業者が普段業務利用しているOA端末など、Intune のアプリ構築に利用している環境上で作業すればOKです。
検証環境の無い片面環境の場合は、先ほどテストしたアプリの割り当てを編集して本番用グループに割り当てます。
(参考:Add, assign, and monitor a Win32 app in Microsoft Intune - Assignments)
もし検証と本番の両面ある場合は、検証環境で動作検証 → 本番環境実装する、という流れになります。
長かったですが、これで実装できました。動作編は以上です!
4. 理想と現実
今回、一瞬のひらめきと「これができたら今後案件で楽できるかも?」という期待を胸に突き進んで検証しました。
その過程で、理想の通りにはいかない現実も見えてきました。
以下に自戒を込めて振り返ります。
当初の理想
スクリプト ツール (仮) 作成にあたり、当初の理想は to have one CSV to rule them all でした。
「CSV さえ用意すれば、あとは脳死で順にスクリプトを実行し、決まった手順で実装できる」という形にしたいと思っていました。
現実
実際にサンプルを作成して検証してみると、以下の気づきがありました。
- 脳死実装は無理
- レジストリ設定の策定が一番重要
- そもそも構築支援スクリプト ツール (仮) は必要なのか?
脳死実装は無理
Intune 実装前のローカル動作検証、実装後のパイロット検証は必須にせざるをえないと気づきました。
今回アイデア勝負でスクリプトを作ったのち、社内でスクリプトの得意な先輩に添削いただきました。その際、過去事例踏まえて「コマンドがうまく動かないレジストリが存在するからちゃんと検証するように」とアドバイスいただきました。
今では、アドバイスと検証結果を踏まえて、具体的に以下の理由から検証は必須にせざるをえないと思っています:
- X-ItemProperty (Set-ItemProperty, New-ItemProperty) で設定できないレジストリがある
- 同様に、reg add で設定できないレジストリもある (別のコマンドにしても対策しきることはできない)
- 今回サンプルで作成したスクリプトの仕様上、設定できないレジストリがある
上記 3. の例を挙げると、ManagedFavorites です。
- キー:HKLM:\SOFTWARE\Policies\Microsoft\Edge
- エントリ:ManagedFavorites
- タイプ:String (REG_SZ) - 値 (例):[{"toplevel_name": "MSSVC LAB"}, {"name": "Intune Company Portal", "url": "portal.manage.microsoft.com"}, {"name": "My Account", "url": "myaccount.microsoft.com"}]
ManagedFavorites では値を JSON 形式で書く必要がありますが、JSON の区切りのためにダブルクォーテーションマークを使う必要があり、スクリプト内で変数に突っ込む際にクオーテーションマークが邪魔して上手く動きませんでした。
(ヒアストリングで入れればよかったかもしれませんが、これのためにスクリプトを修正する元気がありませんでした。)
なお、上の例で使った ManagedFavorites のように、スクリプトで実装できないものの設定カタログで構成できるものもあります。
スクリプト自体を修正したり、reg ファイルに切り替えたりして個別にレジストリ展開するのも手ですが、レジストリ実装対象から外して別手段 (構成プロファイルなど) で制御を行うのも手です。
動作検証を踏まえて、最終形態を検討する必要があると思いました。
レジストリ設定の策定が一番大切
動作検証を踏まえて最終的レジストリ展開対象を吟味するのに加えて、もう少し初期の段階でレジストリ整理を行うのがベストではあります。
お客様から「これを展開したい」と連携されるレジストリの中には、すでに Intune の別機能で実装する予定の制御が含まれる場合があるためです。
別機能の制御の結果設定されるレジストリをさらにアプリやスクリプトとして展開してしまうと、ざっと以下の弊害が予想されます。
- 二重管理になる
- 運用で設定を変更/更新したい場合、2か所更新が必要になる
- トラブル時の事象切り分けが煩雑になる
- 設定が競合する
競合については運用時だけでなく初期構築でも起こりえます。
例えば、Intune 機能設計時にTo-Beモデルとしてシステム設計を刷新しているが現行レジストリはAs-Isモデルで古い値となっている場合、そのまま実装すれば競合します。
こうして Intune 別機能との設定被りを駆逐すると、設定不要になるレジストリが出てくることが多いです。
(本記事冒頭でメンションした例の 100 越えのレジストリは、ほとんど Intune 別機能で設計済みの制御でした。)
また、単純に運用負荷という観点でも、Intune ネイティブの構成プロファイル系の機能を使う方がおすすめです。
現状レジストリは Intune 管理センター に打ち込むことができず、アプリパッケージやスクリプトをアップロードして展開する必要があります。
これらは Intune 管理センターで直接編集することができず、設定を更新したい場合はパッケージやスクリプトをアップロードしなおす必要があります。
さらにアプリのパッケージだと、中身を管理センター上で確認することができないため、設定内容を別紙やメモとして管理していないと簡単にブラックボックス化してしまいます。
「レジストリだと現行の設定を Intune 語に翻訳せずにそのまま流せるから簡単である」というイメージを持たれがちな気がしますが(主観)、それは間違った解釈だと思います。
そもそもスクリプト ツール (仮) は必要なのか?
以上を踏まえると、当初想定していたような 「大量のレジストリを脳死で展開する」シナリオは発生しない のではないかと思うようになりました。
スクリプトを以てしても本番展開なら脳死展開は取りえないリスクとなるし、設計妥当性や運用負荷踏まえレジストリ棚卸をすれば大量のレジストリが展開対象となることもないだろうと思いました。
10 前後のレジストリであれば、検出規則もカスタム検出スクリプトを使わずにGUIで構成したほうがメンテしやすいという、スクリプト ツール (仮) を使わないメリットが勝つシナリオもありそうです。
個人的には、当分このスクリプト ツール (仮) を発動させることもなさそうだなと思いつつ、それはそれとして、とても楽しかったので作ってよかったと思っています。
今日も地球のどこかでレジストリにおぼれそうになっている Intune 担当仲間がいるのではと思ったので、海に瓶を投げる気持ちで記事にしました。
Appendix 1. ご参考:スクリプト ツール (仮) のソースコード
本記事作成時点のソースコードは以下です。
#1 RegItemList.csv サンプル (レジストリ項目リスト)
No,Path,Name,Type,Value
1,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,RestoreOnStartup,DWord,5
2,HKLM:\SOFTWARE\Policies\Microsoft\Edge,NewTabPageContentEnabled,DWord,0
3,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,HomepageLocation,String,portal.office.com
4,HKLM:\SOFTWARE\Policies\Microsoft\Edge,NewTabPageAppLauncherEnabled,DWord,0
5,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,ShowHomeButton,DWord,1
6,HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended,FavoritesBarEnabled,DWord,1
#2 Set-RegProperty.ps1 (レジストリ設定スクリプト)
Set-RegProperty.ps1(長いので折りたたみました。左端の ▶ をクリックして展開)
<#
Set-RegProperty.ps1
imports csv & sets registry accordingly
csv format example:
-----
No,Path,Name,Type,Value
1,HKLM:\Software\Intune,Test1,String,Success
2,HKLM:\Software\Intune,Test2,String,Success
3,HKLM:\Software\Intune,Test3,String,Success
(*repeat)
-----
info: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-itemproperty?view=powershell-7.4
*for "Type" column, use one of the following:
- String: Specifies a null-terminated string. Used for REG_SZ values.
- ExpandString: Specifies a null-terminated string that contains unexpanded references to environment variables that are expanded when the value is retrieved. Used for REG_EXPAND_SZ values.
- Binary: Specifies binary data in any form. Used for REG_BINARY values.
- DWord: Specifies a 32-bit binary number. Used for REG_DWORD values.
- MultiString: Specifies an array of null-terminated strings terminated by two null characters. Used for REG_MULTI_SZ values.
- Qword: Specifies a 64-bit binary number. Used for REG_QWORD values.
- Unknown: Indicates an unsupported registry data type, such as REG_RESOURCE_LIST values.
last updated: 2024/Apr/16
requirement: device-local admin right (*execute in system context)
#>
Set-StrictMode -Version Latest
$ErrorActionPreference = "Continue"
#flush
$flg = [int]0
$ErrReg = @()
#set current directory
Set-Location (Split-Path $MyInvocation.MyCommand.Path -parent)
#import csv
$List = Import-CSV -literalPath ".\RegItemList.csv" -Encoding Default
#for each row, set item (registry) property
$List | foreach {
try {
$No=$_.No
$Path=$_.Path
$Name=$_.Name
$Type=$_.Type
$Value=$_.Value
Write-Host $No
Write-Host $Path
Write-Host $Name
Write-Host $Type
Write-Host $Value
#create reg path if not exist
if ($false -eq (Test-Path -LiteralPath $Path -ErrorAction SilentlyContinue)) {
New-Item -Path $Path -Force | Out-Null
}
New-ItemProperty -LiteralPath $Path -Name $Name -Type $Type -Value $Value -Force -EA Stop
}
catch {
Write-Host "$($_.Exception.Message)"
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
}
If (0 -eq $flg) {
#success message
Write-Host "All reg keys are successfully configured."
exit $flg
} else {
#list reg no. with error
($ErrReg -join ",") | Write-Host
Write-Error ($ErrReg -join ",") -EA Continue
exit $flg
}
#3 New-DetectionScript.ps1 (カスタム検出スクリプト生成スクリプト)
New-DetectionScript.ps1(左端の ▶ をクリックして展開)
<#
New-DetectionScript.ps1
imports csv & creates Intune Win32 app detection script
csv format example:
-----
No,Path,Name,Type,Value
1,HKLM:\Software\Intune,Test1,String,Success
2,HKLM:\Software\Intune,Test2,String,Success
3,HKLM:\Software\Intune,Test3,String,Success
(*repeat)
-----
info: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-itemproperty?view=powershell-7.4
*for "Type" column, use one of the following:
- String: Specifies a null-terminated string. Used for REG_SZ values.
- ExpandString: Specifies a null-terminated string that contains unexpanded references to environment variables that are expanded when the value is retrieved. Used for REG_EXPAND_SZ values.
- Binary: Specifies binary data in any form. Used for REG_BINARY values.
- DWord: Specifies a 32-bit binary number. Used for REG_DWORD values.
- MultiString: Specifies an array of null-terminated strings terminated by two null characters. Used for REG_MULTI_SZ values.
- Qword: Specifies a 64-bit binary number. Used for REG_QWORD values.
- Unknown: Indicates an unsupported registry data type, such as REG_RESOURCE_LIST values.
last updated: 2024/Apr/23
requirement: device-local admin right (*execute in system context)
#>
Set-StrictMode -Version Latest
$ErrorActionPreference = "Continue"
#set current directory
Set-Location (Split-Path $MyInvocation.MyCommand.Path -parent)
#set output path/file
$date = get-date -format "yyyyMMddHHmmss"
$OutputPath = ".\" + $date + "-Detection.ps1"
$DetectionHeader = @'
Set-StrictMode -Version Latest
$ErrorActionPreference = "Continue"
$flg = [int]0
$ErrReg = @()
'@
$DetectionHeader | Out-File $OutputPath -Encoding ascii -Append
$List = Import-CSV -literalPath ".\RegItemList.csv" -Encoding Default
$List | foreach {
$No=$_.No;$Path=$_.Path;$Name=$_.Name;$Type=$_.Type;$Value=$_.Value
$DetectionBody = @"
`$No = `"$No`"
`$Path = `"$Path`"
`$Name = `"$Name`"
`$Type = `"$Type`"
`$Value = `"$Value`"
try {
`$ActualValue = Get-ItemProperty -LiteralPath `$Path -Name `$Name -ErrorAction Stop
`$ActualValue = `$ActualValue.`$Name
if (!(`$ActualValue -eq `$Value)){
`$ErrReg = `$ErrReg + `"Err: `$No`"
`$flg = 1
}
} catch {
`$ErrReg = `$ErrReg + `"Err: `$No`"
`$flg = 1
}
"@ | Out-File $OutputPath -Encoding ascii -Append
}
$DetectionFooter = @'
If (0 -eq $flg) {
#success message
Write-Host "All reg keys are successfully configured."
exit $flg
} else {
#list reg no. with error
($ErrReg -join ",") | Write-Host
Write-Error ($ErrReg -join ",") -EA Continue
exit $flg
}
'@
$DetectionFooter | Out-File $OutputPath -Encoding ascii -Append
#4 yyyyMMddHHmmss-Detection.ps1 サンプル (カスタム検出スクリプト)
yyyyMMddHHmmss-Detection.ps1 サンプル(左端の ▶ をクリックして展開)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Continue"
$flg = [int]0
$ErrReg = @()
$No = "1"
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended"
$Name = "RestoreOnStartup"
$Type = "DWord"
$Value = "5"
try {
$ActualValue = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop
$ActualValue = $ActualValue.$Name
if (!($ActualValue -eq $Value)){
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
} catch {
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
$No = "2"
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
$Name = "NewTabPageContentEnabled"
$Type = "DWord"
$Value = "0"
try {
$ActualValue = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop
$ActualValue = $ActualValue.$Name
if (!($ActualValue -eq $Value)){
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
} catch {
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
$No = "3"
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended"
$Name = "HomepageLocation"
$Type = "String"
$Value = "portal.office.com"
try {
$ActualValue = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop
$ActualValue = $ActualValue.$Name
if (!($ActualValue -eq $Value)){
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
} catch {
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
$No = "4"
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
$Name = "NewTabPageAppLauncherEnabled"
$Type = "DWord"
$Value = "0"
try {
$ActualValue = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop
$ActualValue = $ActualValue.$Name
if (!($ActualValue -eq $Value)){
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
} catch {
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
$No = "5"
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended"
$Name = "ShowHomeButton"
$Type = "DWord"
$Value = "1"
try {
$ActualValue = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop
$ActualValue = $ActualValue.$Name
if (!($ActualValue -eq $Value)){
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
} catch {
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
$No = "6"
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended"
$Name = "FavoritesBarEnabled"
$Type = "DWord"
$Value = "1"
try {
$ActualValue = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop
$ActualValue = $ActualValue.$Name
if (!($ActualValue -eq $Value)){
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
} catch {
$ErrReg = $ErrReg + "Err: $No"
$flg = 1
}
If (0 -eq $flg) {
#success message
Write-Host "All reg keys are successfully configured."
exit $flg
} else {
#list reg no. with error
($ErrReg -join ",") | Write-Host
Write-Error ($ErrReg -join ",") -EA Continue
exit $flg
}
Appendix 2. 参考にしたサイト
-
HKLM のレジストリ設定を対象としている理由:HKCUキーのレジストリ設定を以前検証した際、Win32アプリとして展開するにはユーザーコンテキスト実行が必要・かつ端末を利用するエンドユーザーアカウントに端末のローカル管理者権限が必要と、HKLMキー展開よりも前提条件が多くなる結果となった (このため、エンドユーザーから管理者権限をはく奪している場合は設定できない)。また、同じようにエンドユーザーから管理者権限をはく奪しているシナリオで HKU 下のデフォルトのユーザーレジストリ設定をするには、Autopilot ユーザードリブンモードのデバイスのセットアップフェーズで実行する必要があるなど、こちらも環境前提が限定的になるという検証結果となった。このため本記事では実行環境を選ばない HKLM に限定して記載している。 ↩