Microsoft Store旧バージョンをSCCMタスクシーケンスとPowerShellで完全削除する方法

SCCM(Microsoft Endpoint Configuration Manager)のタスク シーケンスで Windows 10/11 を展開したあと、「Microsoft.MPEG2VideoExtension」などの Microsoft Store アプリ旧バージョンだけがしぶとく残ってしまい、再ログオンのたびに復活して困る…というケースは少なくありません。本記事では、PowerShell とタスク シーケンスを組み合わせて、すべてのユーザープロファイルから旧版 Store アプリを確実に削除する方法と、エラー時の対処までを詳しく解説します。

目次

Microsoft Store アプリ旧バージョンが消えない理由

まずは「なぜ Remove-AppxPackage -AllUsers を実行したのに、旧バージョンが残るのか?」という原因から整理します。仕組みを理解しておくと、トラブルシュートが格段にやりやすくなります。

Appx の2つの顔:Provisioned と User インストール

Microsoft Store アプリ(UWP アプリ)は、概ね次の 2 層構造で管理されています。

  • Provisioned Package(プロビジョニング)
    OS イメージに「このアプリは新規ユーザープロファイル作成時に自動配布せよ」として登録されている層。
    Get-AppxProvisionedPackage -Online で確認可能です。
  • ユーザーごとの AppxPackage
    実際にユーザープロファイル(SID)単位でインストールされている層。
    Get-AppxPackage -User <SID> や -AllUsers で確認できます。

この構造のせいで、次のような現象が起きます。

  • Provisioned を削除していない → 新規ログオンユーザーでアプリが「復活」する
  • Remove-AppxPackage -AllUsers 実行時、SYSTEM アカウントから見えないプロファイルが存在する → 一部ユーザー分が消えない

したがって、確実に旧バージョンを消したい場合は、

「列挙 → Provisioned 削除 → 既存ユーザー削除」

という 3 段構えが必須になります。

SCCM タスク シーケンスからの実行特有の落とし穴

SCCM のタスク シーケンス(OS 展開)では、PowerShell ステップのほとんどが Local System アカウントで実行されます。このとき、

  • まだ作成されていないユーザープロファイルの SID にはアクセスできない
  • ドメインユーザーの一部が OS 展開時点では存在せず、後から参加する可能性がある

といった事情があり、単に -AllUsers を 1 回実行しただけでは「取りこぼし」が発生します。この記事のスクリプトは、この「取りこぼし」を限りなくゼロに近づける設計になっています。

前提条件と想定環境

この記事で紹介する手順は、以下のような環境を前提にしています(あくまで一例です)。

項目例補足
OSWindows 10 / Windows 11Microsoft Store アプリがプリインストールされているビルド
管理ツールSCCM(MECM) タスク シーケンスOS 展開タスク シーケンスで PowerShell スクリプトを実行
実行アカウントLocal SystemTS の標準実行コンテキスト
対象アプリ例Microsoft.MPEG2VideoExtension旧バージョンのみを完全削除したいケースを想定

全体像:Store アプリ旧バージョン削除の流れ

先に、この記事で解説する全体フローを表形式で整理しておきます。

手順目的代表コマンドポイント
① パッケージ名の特定全ユーザー分の Appx を列挙し、正確な PackageFullName を把握Get-AppxPackage -AllUsers名前とバージョン、アーキテクチャを誤認しないことが重要
② Provisioned の削除新規ユーザー作成時の自動インストールを無効化Remove-AppxProvisionedPackage -OnlineDisplayName で一致させて削除。存在しない場合はスキップ可能
③ 既存ユーザープロファイルから削除全ての既存 SID から対象アプリをアンインストールGet-CimInstance Win32_UserProfile
Remove-AppxPackage -User
SID を列挙してユーザーごとに確実に削除する
④ TS への組み込みOS 展開時に自動実行されるよう PowerShell ステップを追加powershell.exe -ExecutionPolicy Bypass -File …「Setup Windows and ConfigMgr」の後に配置するのが定番
⑤ ログで検証削除前後で Appx 一覧を取得し、差分を確認Start-Transcript
Out-File
後から原因追跡できるよう、ログを TS ログと一緒に残す

パッケージ名の特定:まずは「何が入っているか」を見抜く

最初のステップは、「どのバージョンのどのパッケージ名を消すのか」を正確に特定することです。ここを曖昧にすると、

  • 本来消したくない新バージョンまで消してしまう
  • 存在しない PackageFullName を指定して、スクリプトがエラーだらけになる

といった事故につながります。

全ユーザー分の Appx 一覧を採取する例

まずは管理端末で、全ユーザー分の Appx 一覧を採取してみましょう。

Get-AppxPackage -AllUsers `
    | Select-Object Name, PackageFullName, Version `
    | Sort-Object Name, Version `
    | Out-File -FilePath C:\Temp\Appx_AllUsers.txt -Encoding UTF8

このファイルを開き、例えば MPEG2 で検索すると、次のようなパターンが見つかります。

  • Microsoft.MPEG2VideoExtension_1.0.22661.0_x64__8wekyb3d8bbwe
  • Microsoft.MPEG2VideoExtension_2.0.XXXXX.0_x64__8wekyb3d8bbwe

ここで「旧バージョンだけを削除し、新バージョンは残したい」といった要件がある場合は、Version の列をよく確認してから削除対象を決めましょう。

ワイルドカードで候補を絞るワンライナー

手早く候補だけ見たい場合は、ワイルドカード検索も有効です。

Get-AppxPackage -AllUsers `
    | Where-Object { $_.Name -like '*MPEG2*' } `
    | Select-Object Name, PackageFullName, Version

同様に Provisioned パッケージも確認しておきます。

Get-AppxProvisionedPackage -Online `
    | Where-Object { $_.DisplayName -like '*MPEG2*' } `
    | Select-Object DisplayName, PackageName, Version

ここで表示される DisplayName や PackageName をメモしておき、次のステップで使用します。

Provisioned パッケージの削除:新規ユーザーへの再配布を止める

旧バージョンを完全に排除したいのであれば、Provisioned パッケージを削除しておくことが必須です。これを消さないまま OS を展開すると、

  • OS 展開直後に TS で削除しても、その後にログオンしたユーザーで自動インストールされる

という「いたちごっこ」が発生します。

Provisioned パッケージ削除の基本スクリプト

例として Microsoft.MPEG2VideoExtension を削除する場合の基本形は次の通りです。

$targetDisplayName = 'Microsoft.MPEG2VideoExtension'

$prov = Get-AppxProvisionedPackage -Online `
    | Where-Object { $_.DisplayName -eq $targetDisplayName }

if ($prov) {
    foreach ($p in $prov) {
        Write-Host "Removing provisioned package: $($p.DisplayName) / $($p.PackageName)"
        Remove-AppxProvisionedPackage -Online -PackageName $p.PackageName -ErrorAction Stop
    }
}
else {
    Write-Host "Provisioned package '$targetDisplayName' は見つかりませんでした。スキップします。"
}

ポイントは、存在しない場合は素直にスキップすることです。タスク シーケンス内で実行するスクリプトは、Write-Error 連発で TS 全体が失敗してしまうと運用負荷が上がります。存在チェックを入れ、問題ない場合は優しくスキップする設計が実務的です。

既存ユーザープロファイルからの削除:SID を列挙して確実に消す

次のステップは、既に存在する全ユーザープロファイルから対象アプリを削除することです。ここが最も取りこぼしが出やすいポイントです。

なぜ -AllUsers だけでは不十分なのか

Remove-AppxPackage -AllUsers は便利に見えますが、SYSTEM コンテキストで実行した場合、実際には次のようなケースが発生することがあります。

  • 一部のユーザープロファイルが「バックアップ」として残っており、SYSTEM から見えない状態になっている
  • ドメインユーザーのプロファイルが、OS 展開時点ではまだ作成されていない

特に前者のパターンでは、「一部のユーザーだけ旧バージョンが残っている」という気持ち悪い状態になります。そこで、Win32_UserProfile から SID を列挙し、その SID ごとに Appx を削除する方式を取ります。

SID 列挙+ユーザーごとの削除スクリプト例

単一アプリを対象とした、シンプルな例を示します。

$AppName = 'Microsoft.MPEG2VideoExtension'

# 一般ユーザープロファイルのみを抽出(システム用・テンプレは除外)
$profiles = Get-CimInstance Win32_UserProfile `
    | Where-Object { -not $_.Special -and $_.LocalPath -like 'C:\Users\*' }

foreach ($profile in $profiles) {
    $sid = $profile.SID
    Write-Host "ユーザー SID: $sid の Appx を確認中..."

    $packages = Get-AppxPackage -User $sid -Name $AppName -ErrorAction SilentlyContinue

    if ($packages) {
        foreach ($pkg in $packages) {
            Write-Host "  削除対象: $($pkg.PackageFullName)"
            try {
                Remove-AppxPackage -User $sid -Package $pkg.PackageFullName -ErrorAction Stop
                Write-Host "  → 削除に成功しました。"
            }
            catch {
                Write-Warning "  → 削除に失敗しました: $_"
            }
        }
    }
    else {
        Write-Host "  対象アプリはインストールされていません。"
    }
}

ここでも、存在しない場合はエラーにしないように -ErrorAction SilentlyContinue や if ($packages) で丁寧に分岐させるのがポイントです。

複数アプリを一括で扱えるようにスクリプトを汎用化する

実運用では、「MPEG2 拡張」だけでなく、他の Store アプリも同時に整理したくなることが多いはずです。そのたびにスクリプトをコピペで増やしていくと、すぐにメンテナンス不能になります。

そこで、対象アプリを配列で持ち、1 本のスクリプトでループ削除できるようにしておくと、運用が格段に楽になります。

汎用スクリプトの全体例

以下は、Provisioned 削除+ユーザー削除を 1 本で実行する PowerShell スクリプトの例です。タスク シーケンスからもそのまま呼び出せる構成になっています。

# ------------------------------------------------------------
# 旧バージョン Store アプリ削除スクリプト(例)
# ------------------------------------------------------------

# ログ出力先
$LogRoot = 'C:\Windows\Temp\StoreAppCleanup'
if (-not (Test-Path $LogRoot)) {
    New-Item -Path $LogRoot -ItemType Directory -Force | Out-Null
}
$LogPath = Join-Path $LogRoot 'StoreAppCleanup.log'

Start-Transcript -Path $LogPath -Append

# 対象アプリの一覧(Name / DisplayName)
$TargetApps = @(
    [PSCustomObject]@{
        Name        = 'Microsoft.MPEG2VideoExtension'
        DisplayName = 'Microsoft.MPEG2VideoExtension'
    }
    # 必要に応じてここに追加
    # [PSCustomObject]@{ Name = 'xxxx'; DisplayName = 'xxxx' }
)

Write-Host '==== Provisioned パッケージの削除開始 ===='

foreach ($app in $TargetApps) {
    Write-Host "対象アプリ: $($app.Name)"

    # Provisioned 削除
    $prov = Get-AppxProvisionedPackage -Online `
        | Where-Object { $_.DisplayName -eq $app.DisplayName }

    if ($prov) {
        foreach ($p in $prov) {
            Write-Host "  Remove-AppxProvisionedPackage: $($p.PackageName)"
            try {
                Remove-AppxProvisionedPackage -Online -PackageName $p.PackageName -ErrorAction Stop
                Write-Host "  → Provisioned 削除成功"
            }
            catch {
                Write-Warning "  → Provisioned 削除失敗: $_"
            }
        }
    }
    else {
        Write-Host "  Provisioned は登録されていません。スキップ。"
    }
}

Write-Host '==== Provisioned パッケージの削除完了 ===='

Write-Host '==== 既存ユーザーからの削除開始 ===='

# 一般ユーザープロファイルのみ抽出
$profiles = Get-CimInstance Win32_UserProfile `
    | Where-Object { -not $_.Special -and $_.LocalPath -like 'C:\Users\*' }

foreach ($profile in $profiles) {
    $sid = $profile.SID
    Write-Host "ユーザー SID: $sid"

    foreach ($app in $TargetApps) {
        $packages = Get-AppxPackage -User $sid -Name $app.Name -ErrorAction SilentlyContinue

        if ($packages) {
            foreach ($pkg in $packages) {
                Write-Host "  Remove-AppxPackage: $($pkg.PackageFullName)"
                try {
                    Remove-AppxPackage -User $sid -Package $pkg.PackageFullName -ErrorAction Stop
                    Write-Host "  → ユーザー削除成功"
                }
                catch {
                    Write-Warning "  → ユーザー削除失敗: $_"
                }
            }
        }
        else {
            Write-Host "  対象アプリは見つかりません。"
        }
    }
}

Write-Host '==== 既存ユーザーからの削除完了 ===='

Stop-Transcript

このスクリプトを Remove-OldStoreApps.ps1 などの名前で保存し、SCCM パッケージ化してタスク シーケンスから呼び出せば、1 つのステップで複数アプリをまとめて削除できます。

SCCM タスク シーケンスへの組み込み手順

続いて、上記のスクリプトをタスク シーケンスに組み込む具体的な手順を解説します。

スクリプトのパッケージ化

  1. 共有フォルダー(例:\\SCCMServer\Sources\Scripts)を用意
  2. Remove-OldStoreApps.ps1 を格納
  3. SCCM コンソールで「パッケージ」を新規作成し、ソースフォルダーとして上記共有を指定
  4. プログラムは必須ではないため、空のままでも構いません(TS の「パッケージ内のスクリプトを実行」オプションを使用)

タスク シーケンスでのステップ配置

推奨される配置は、次のとおりです。

  1. 「Install Operating System」ステップ群
  2. 「Apply Network Settings」「Apply Device Drivers」など
  3. 「Setup Windows and ConfigMgr」
  4. (ここで)PowerShell スクリプト実行ステップを追加
  5. アプリケーションのインストール、その他カスタマイズ

「Setup Windows and ConfigMgr」より前に置いてしまうと、CCM クライアント周りの処理とタイミングがかち合い、トラブルの原因になることがあります。特に理由がなければ、上記の順序が安全です。

PowerShell ステップの設定例

タスク シーケンスのステップ設定は、例えば次のようにします。

項目設定例備考
ステップの種類Run PowerShell Script日本語環境では「PowerShell スクリプトの実行」
スクリプト ソースパッケージ内のスクリプトを使用事前に作成したパッケージを指定
スクリプト名Remove-OldStoreApps.ps1パッケージのルートに配置した場合
実行ポリシーBypassスクリプトブロックを回避
実行アカウントデフォルト(Local System)特別な理由がない限り変更不要
64ビット PowerShell有効にするWindows 10/11 では 64bit での実行を推奨

コマンドライン指定で実行したい場合は、次のような形式でも構いません。

powershell.exe -ExecutionPolicy Bypass -File ".\Remove-OldStoreApps.ps1"

ログ出力と検証:削除前後を比較できるようにする

OS 展開トラブルの多くは、「後から証拠がない」ことによって原因追跡が難航します。Store アプリ削除も例外ではありません。スクリプト内でログを丁寧に残しておくことで、トラブルシュートが圧倒的に楽になります。

Transcript と Appx 一覧ログの併用

先ほどのサンプルスクリプトでは、Transcript ログをすでに使用しています。さらに精度を高めるには、削除前後で Appx 一覧を採取し、差分をとれるようにしておくと便利です。

$LogRoot = 'C:\Windows\Temp\StoreAppCleanup'
if (-not (Test-Path $LogRoot)) {
    New-Item -Path $LogRoot -ItemType Directory -Force | Out-Null
}

# 削除前の一覧
Get-AppxPackage -AllUsers `
    | Select-Object Name, PackageFullName, Version `
    | Sort-Object Name, Version `
    | Out-File (Join-Path $LogRoot 'Appx_Before.txt') -Encoding UTF8

# ...(削除処理)...

# 削除後の一覧
Get-AppxPackage -AllUsers `
    | Select-Object Name, PackageFullName, Version `
    | Sort-Object Name, Version `
    | Out-File (Join-Path $LogRoot 'Appx_After.txt') -Encoding UTF8

タスク シーケンスのあと、これらのログを C:\Windows\CCM\Logs など SCCM クライアントのログと同じ場所にコピーしておけば、現地調査なしに状態を把握しやすくなります。

エラー発生時の対処とトラブルシュート

ここからは、実際に運用していると発生しがちなトラブルと、その対処方法をまとめます。

代表的なトラブルと対処一覧

症状主な原因確認ポイント対処
TS 実行後も一部ユーザーで旧アプリが残る-AllUsers だけで処理している/一部 SID が処理対象外Win32_UserProfile の一覧とスクリプトのループ対象を比較SID 列挙方式でユーザーごとに -User 指定を行うよう修正
新規ログオンユーザーでだけアプリが復活するProvisioned パッケージが残っているGet-AppxProvisionedPackage -Online で対象が残っていないか確認Provisioned 削除のステップを追加し、TS の前半で実行
スクリプトが 0x80073CFA などのエラーを返すOS によって保護されている Appx/InboxWin32 化されているイベントログ・DISM で Capability の有無を確認Appx ではなく「Windows Capability」として削除を検討
TS ステップが失敗扱いになる存在しないパッケージ名で Remove-AppxPackage を実行ログに「見つかりませんでした」系のエラーメッセージが残っていないか確認事前に if ($pkg) で存在チェックを行い、エラーを抑制

タイミングの問題:ユーザープロファイルがない段階で -User を実行している

タスク シーケンスのフェーズによっては、まだユーザープロファイル自体が存在しない場合があります。この状態で Get-AppxPackage -User <SID> を呼び出すと、当然ながら失敗します。

OS 展開 TS であれば、概ね次のタイミングが安全です。

  • 「Setup Windows and ConfigMgr」実行後
  • OS の初回再起動前後

このタイミングであれば、ローカル Administrator や OS が内部的に使用するプロファイルがすでに作成されていることが多く、SID 列挙が可能です。

誤ったパッケージ名を指定している

32bit 版/64bit 版、Store 版/非 Store 版の混在などにより、似たようなパッケージ名が複数存在することがあります。特に Name と PackageFullName を取り違えると、削除対象が思わぬものになってしまうことがあります。

安全のためには、次のような確認を徹底すると良いでしょう。

  • Get-AppxPackage -AllUsers | Where-Object { $_.Name -like '*MPEG2*' } で候補を絞る
  • 削除対象は「Name ベース」で管理し、内部的には PackageFullName に展開してから削除する
  • テスト端末で「Before/After ログ」を比較し、想定外のアプリが消えていないか必ず確認する

InboxWin32 化されたアプリ(Windows 11 22H2 以降)への対応

Windows 11 22H2 以降では、一部アプリが UWP アプリ(Appx)ではなく、Windows Capability(InboxWin32) として扱われるケースがあります。この場合、Get-AppxPackage には出てこないため、Appx 系コマンドでは一切操作できません。

このようなアプリを削除したい場合は、次のように DISM で Capability を確認し、必要に応じて削除します。

dism /Online /Get-Capabilities | findstr /i "media"

削除例(あくまでイメージ):

dism /Online /Remove-Capability /CapabilityName:XXXXX~~~~0.0.1.0

Capability 名は環境によって異なるため、実際にはテスト環境で慎重に確認してから本番へ展開することを強くおすすめします。

Provisioned が 0 件なのにアンインストールできない場合

時折、「Get-AppxProvisionedPackage -Online では対象が 0 件なのに、Remove-AppxPackage がエラーになる」というパターンがあります。この場合、OS 側でそのアプリがシステム保護されている、あるいは OS バージョンとアプリの組み合わせに依存する挙動になっている可能性があります。

こうしたケースでは、

  • OS のビルドアップデート(Feature Update)後に再度スクリプトを実行する
  • 該当アプリを「残しておく」運用に切り替える

など、OS 全体のライフサイクルと合わせて検討する必要があります。

運用のコツとベストプラクティス

最後に、長期的な運用を意識したコツをまとめます。

1本のスクリプトで「列挙 → Provisioned 削除 → ユーザー削除」まで完結させる

ステップをバラバラのスクリプトに分けてしまうと、

  • どの TS がどのバージョンのスクリプトを使っているのか分からなくなる
  • アプリ追加時に複数スクリプトを同時修正しなければならない

といった運用負債を生みます。この記事のように、1 本のスクリプトの中で、

  1. Appx / Provisioned の列挙(前後ログ取得)
  2. Provisioned 削除
  3. 既存ユーザーからの削除

を一気通貫で行う構成にしておくと、保守性が高まります。

対象アプリは配列で管理し、コメントで用途を明記

「なぜこのアプリを消すのか」が分からなくなると、数年後に自分や後任が困ります。対象アプリ定義部分は、例えば次のようにコメントを丁寧に入れておくとよいでしょう。

$TargetApps = @(
    [PSCustomObject]@{
        Name        = 'Microsoft.MPEG2VideoExtension'
        DisplayName = 'Microsoft.MPEG2VideoExtension'
        # 旧 MPEG2 拡張。2024年度より社内標準では不要のため削除
    }
    [PSCustomObject]@{
        Name        = 'Contoso.LegacyApp'
        DisplayName = 'Contoso.LegacyApp'
        # Contoso 社製旧アプリ。後継の Win32 版へ統一
    }
)

ログは TS のログフォルダーへ集約する

サーバー側から一括確認しやすくするため、スクリプトの最後にログを CCM ログ配下へコピーしておくのも有効です。

$CcmLog = 'C:\Windows\CCM\Logs'
if (Test-Path $CcmLog) {
    Copy-Item -Path "$LogRoot\*" -Destination $CcmLog -Force
}

こうしておけば、SCCM コンソールからクライアントログを取得するだけで、Store アプリ削除の詳細ログも同時に参照できます。

パイロット コレクションで必ず検証してから本番展開

Store アプリの削除は、場合によっては業務アプリケーションに影響することがあります。特にメディア関連や PDF 閲覧アプリなど、ユーザーが日常的に使用するアプリを削除する場合は要注意です。

本番コレクションに適用する前に、必ず次のようなステップを踏みましょう。

  • IT 部門用のパイロットコレクションを作成し、限定的に TS を展開
  • 代表的な業務アプリ(ブラウザー、Office、業務システム)との併用検証を実施
  • ユーザー代表者にテスト協力を依頼し、実際の業務フローで問題がないか確認

この一手間を惜しむと、後から「実はあのアプリが消えて困っていた」という声が上がり、巻き戻しに多大なコストがかかることになります。

まとめ:SCCM + PowerShell で旧版 Store アプリを漏れなく削除する

本記事では、Remove-AppxPackage -AllUsers だけでは取りこぼしが出てしまう Microsoft Store アプリの旧バージョンを、SCCM タスク シーケンスと PowerShell を使って「漏れなく」「再発しないように」削除する方法を解説しました。

  • まずは全ユーザー/Provisioned の状態を列挙して、正確なパッケージ名を把握する
  • Provisioned パッケージを削除して、新規ユーザーへの「復活」を防ぐ
  • Win32_UserProfile から SID を列挙し、各ユーザーに対して -User 指定で Appx を削除する
  • これらを 1 本のスクリプトにまとめ、タスク シーケンスの「Setup Windows and ConfigMgr」後に実行する
  • ログ(Transcript と Appx 一覧)を残し、パイロット コレクションで十分に検証してから本番展開する

この流れをテンプレート化しておけば、今後新たに「削除したい Store アプリ」が出てきたときも、配列に 1 行追加するだけで同じ仕組みを再利用できます。Microsoft Store アプリの管理を標準化し、クリーンな OS 展開イメージを維持するための一助になれば幸いです。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次