PowerShell 7.6.4でStart-ThreadJobが失敗する原因とモジュール名変更の対応方法

PowerShell 7.6.4へ更新した後、ThreadJob\Start-ThreadJobを使う既存スクリプトが失敗する場合は、モジュール修飾名を次のように変更します。

Microsoft.PowerShell.ThreadJob\Start-ThreadJob

修正が必要なのは、基本的にThreadJob\という旧モジュール名を明示している部分です。Start-ThreadJobコマンドレット自体の名前やパラメーター、ジョブの受信方法は変わっていません。モジュール名を付けずにStart-ThreadJobと呼び出しているスクリプトへの影響も、通常はありません。

Microsoftはこの変更をPowerShell 7.6.4の破壊的変更として案内しており、同バージョンにはMicrosoft.PowerShell.ThreadJob 2.2.0が含まれます。(Microsoft Learn)

目次

結論:旧モジュール名を新しい名前へ置き換える

最も重要な修正点は次のとおりです。

対象PowerShell 7.6.4での対応
ThreadJob\Start-ThreadJobMicrosoft.PowerShell.ThreadJob\Start-ThreadJobへ変更
Start-ThreadJob原則として変更不要
Import-Module ThreadJobImport-Module Microsoft.PowerShell.ThreadJobへの変更を検討
#Requires -Modules ThreadJob新しいモジュール名へ変更
モジュールマニフェストのRequiredModules旧名が指定されていないか確認
using module ThreadJob新しいモジュール名へ変更

既存コードが次のようになっている場合、

$job = ThreadJob\Start-ThreadJob -ScriptBlock {
    Get-Process
}

PowerShell 7.6.4では次のように修正します。

$job = Microsoft.PowerShell.ThreadJob\Start-ThreadJob -ScriptBlock {
    Get-Process
}

変更するのはThreadJobの部分だけです。-ScriptBlock-ArgumentList-ThrottleLimitなどの使い方を変更する必要はありません。

PowerShell 7.6.4で失敗する原因

ThreadJobモジュールがMicrosoft.PowerShell.ThreadJobに置き換わった

PowerShell 7.6.4では、従来のThreadJobモジュールがMicrosoft.PowerShell.ThreadJobモジュールに置き換えられました。

一方、Start-ThreadJobコマンドレットそのものは引き続き提供されています。つまり、今回の問題はコマンドレットの削除や仕様変更ではなく、コマンドレットを提供するモジュールの名前が変わったことによって発生します。(Microsoft Learn)

モジュール修飾名は正確なモジュール名を指定する

PowerShellでは、次の形式をモジュール修飾名として使用できます。

モジュール名\コマンド名

たとえば、次の記述は「ThreadJobという名前のモジュールからStart-ThreadJobを実行する」という明示的な指定です。

ThreadJob\Start-ThreadJob

PowerShell 7.6.4の標準環境ではモジュール名がMicrosoft.PowerShell.ThreadJobに変わったため、PowerShellが旧名のThreadJobを解決できず、モジュール読み込みエラーになります。

環境や表示言語によって詳細は異なりますが、次のようなエラーとして現れることがあります。

The module 'ThreadJob' could not be loaded.
For more information, run 'Import-Module ThreadJob'.

モジュール修飾名は別名や互換用のラベルではありません。指定されたモジュールを明示的に解決するため、同じStart-ThreadJobが別モジュールに存在していても、旧モジュール名から新モジュール名へ自動的に読み替えられるわけではありません。

PowerShell公式ドキュメントでも、モジュール修飾名はコマンドの提供元を明示する方法として説明されています。区切り文字には、Windows、Linux、macOSのいずれでもバックスラッシュを使用します。(Microsoft Learn)

影響を受ける呼び出しと受けない呼び出し

影響を受ける呼び出し

次のように、旧モジュール名を明示しているコードは修正対象です。

ThreadJob\Start-ThreadJob -ScriptBlock {
    Get-Date
}

また、直接のコマンド呼び出しだけでなく、次の記述も確認してください。

Import-Module ThreadJob
#Requires -Modules ThreadJob
using module ThreadJob

モジュールマニフェストでは、次のような指定が残っている可能性があります。

RequiredModules = @(
    'ThreadJob'
)

PowerShell 7.6.4を前提にする場合は、次のように変更します。

RequiredModules = @(
    'Microsoft.PowerShell.ThreadJob'
)

原則として影響を受けない呼び出し

次のように、モジュール名を付けずに呼び出している場合は、通常そのまま利用できます。

Start-ThreadJob -ScriptBlock {
    Get-Date
}

PowerShellは、現在のセッションにStart-ThreadJobが読み込まれていなければ、利用可能なモジュールからコマンドを検索し、モジュールの自動読み込みが有効な環境では該当モジュールを読み込みます。

ただし、未修飾呼び出しが必ず安全とは限りません。次のような環境では確認が必要です。

  • $PSModuleAutoLoadingPreferenceで自動読み込みを無効にしている
  • 同名の関数やエイリアスを定義している
  • 複数のモジュールが同名コマンドを提供している
  • 制限付きセッションで利用可能なコマンドを絞っている
  • 実行ユーザーごとにPSModulePathが異なる
  • 開発端末とタスクスケジューラ、CI/CD環境でモジュール構成が異なる

PowerShellでは、未修飾のコマンド名が重複した場合、エイリアス、関数、コマンドレットなどの優先順位に従って実行対象が決まります。提供元を固定したいスクリプトでは、モジュール修飾名を利用する意味があります。(Microsoft Learn)

現在のPowerShellとモジュール状態を確認する

修正前に、実際にどのPowerShellとモジュールが使われているかを確認します。

PowerShellのバージョンを確認する

$PSVersionTable.PSVersion

表示例は次のようになります。

Major  Minor  Patch
-----  -----  -----
7      6      4

複数のPowerShellがインストールされている端末では、Windows PowerShell 5.1とPowerShell 7が共存していることがあります。ターミナルで確認するだけでなく、タスクスケジューラやCI/CDが実際に呼び出している実行ファイルも確認してください。

PowerShell 7の実行ファイルは通常pwshです。Windows PowerShell 5.1はpowershell.exeで起動されます。

利用可能なThreadJob関連モジュールを確認する

Get-Module -ListAvailable -Name ThreadJob, Microsoft.PowerShell.ThreadJob |
    Sort-Object Name, Version -Descending |
    Select-Object Name, Version, ModuleBase

PowerShell 7.6.4の標準構成では、次のモジュールが確認対象になります。

Name                            Version
----                            -------
Microsoft.PowerShell.ThreadJob  2.2.0

Get-Module -ListAvailableは、現在読み込まれているモジュールだけでなく、PSModulePath上にあり、読み込み可能なモジュールを調べるために使用できます。(Microsoft Learn)

旧名のThreadJobも表示される場合は、ユーザー領域や共有モジュールフォルダーに旧モジュールが別途インストールされている可能性があります。

その場合、端末によって旧コードが動いたり動かなかったりする状態になりやすいため、ModuleBaseも確認してください。

Start-ThreadJobの提供元を確認する

Get-Command Start-ThreadJob -All |
    Select-Object CommandType, Name, Version, Source, ModuleName

PowerShell 7.6.4で確認したい提供元は次の名前です。

Microsoft.PowerShell.ThreadJob

新しいモジュール修飾名を直接解決できるか確認する場合は、次を実行します。

Get-Command 'Microsoft.PowerShell.ThreadJob\Start-ThreadJob' `
    -ErrorAction Stop |
    Select-Object Name, Version, Source, ModuleName

旧名も確認すると、違いを切り分けやすくなります。

Get-Command 'ThreadJob\Start-ThreadJob' -ErrorAction Stop

このコマンドだけが失敗し、新しいモジュール修飾名では成功する場合、今回の名称変更による影響と判断できます。

既存スクリプトを修正する手順

旧モジュール修飾名を検索する

まず、.ps1.psm1.psd1ファイルから旧記述を検索します。

$files = Get-ChildItem -Path C:\Scripts `
    -Recurse `
    -File `
    -Include *.ps1, *.psm1, *.psd1

$oldQualifiedName = '(?i)(?<![\w.])ThreadJob\\Start-ThreadJob'

$files | Select-String -Pattern $oldQualifiedName

正規表現で直前が英数字やドットではないことを確認しているため、修正済みの次の記述は検索結果から除外できます。

Microsoft.PowerShell.ThreadJob\Start-ThreadJob

単純にThreadJob\Start-ThreadJobという文字列を検索すると、新しい名前の一部分にも一致してしまいます。検索結果をそのまま一括置換すると、次のような誤った名前を作る可能性があるため注意してください。

Microsoft.PowerShell.Microsoft.PowerShell.ThreadJob\Start-ThreadJob

モジュール名を参照している箇所も確認する

直接呼び出し以外の参照を確認するには、対象ファイル内のThreadJobを広く検索します。

$files | Select-String -Pattern '(?i)\bThreadJob\b'

特に次の記述を目視で確認してください。

Import-Module ThreadJob
#Requires -Modules ThreadJob
using module ThreadJob
RequiredModules = @('ThreadJob')

コメントや手順書に書かれた旧モジュール名も、運用担当者が古いコマンドを実行する原因になります。コードだけでなく、README、デプロイ手順、ジョブ登録手順も確認すると安全です。

モジュール修飾名を置換する

修正前は次のコードです。

$jobs = 1..4 | ForEach-Object {
    ThreadJob\Start-ThreadJob -ArgumentList $_ -ScriptBlock {
        param($number)

        [pscustomobject]@{
            Number = $number
            Square = $number * $number
        }
    }
}

$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job

修正後は次のようになります。

$jobs = 1..4 | ForEach-Object {
    Microsoft.PowerShell.ThreadJob\Start-ThreadJob `
        -ArgumentList $_ `
        -ScriptBlock {
            param($number)

            [pscustomobject]@{
                Number = $number
                Square = $number * $number
            }
        }
}

$jobs | Wait-Job | Receive-Job
$jobs | Remove-Job

Wait-JobReceive-JobRemove-Jobの処理は変更不要です。

明示的なImport-Moduleも変更する

旧コードが次のようになっている場合、

Import-Module ThreadJob -ErrorAction Stop

PowerShell 7.6.4向けには次のように変更します。

Import-Module Microsoft.PowerShell.ThreadJob -ErrorAction Stop

その後、未修飾で呼び出すこともできます。

Import-Module Microsoft.PowerShell.ThreadJob -ErrorAction Stop

$job = Start-ThreadJob -ScriptBlock {
    Get-Date
}

提供元まで固定したい場合は、インポート後も新しいモジュール修飾名を使用します。

Import-Module Microsoft.PowerShell.ThreadJob -ErrorAction Stop

$job = Microsoft.PowerShell.ThreadJob\Start-ThreadJob -ScriptBlock {
    Get-Date
}

UTF-8のスクリプトを一括置換する方法

大量のスクリプトを修正する場合は、事前にGitへコミットするか、対象フォルダーをバックアップしてください。

次の例は、UTF-8で管理されているPowerShellファイルを対象に、旧モジュール修飾名を置換します。

$files = Get-ChildItem -Path C:\Scripts `
    -Recurse `
    -File `
    -Include *.ps1, *.psm1, *.psd1

$pattern = '(?i)(?<![\w.])ThreadJob\\Start-ThreadJob'
$replacement = 'Microsoft.PowerShell.ThreadJob\Start-ThreadJob'

foreach ($file in $files) {
    $content = Get-Content -LiteralPath $file.FullName -Raw
    $updated = [regex]::Replace($content, $pattern, $replacement)

    if ($updated -cne $content) {
        Set-Content -LiteralPath $file.FullName `
            -Value $updated `
            -Encoding utf8NoBOM `
            -NoNewline

        Write-Host "Updated: $($file.FullName)"
    }
}

この正規表現は、新しいモジュール名の中に含まれるThreadJob\Start-ThreadJobを再置換しないようにしています。

ただし、元ファイルがShift_JIS、UTF-16、UTF-8 BOM付きなどの場合、この例を実行すると文字コードがUTF-8 BOMなしに変わります。既存の文字コードを維持する必要がある環境では、Visual Studio Codeなどの文字コードを保持できるエディターで置換してください。

修正後に実行する動作確認

コマンドを単体で実行する

最初に、最小構成でスレッドジョブが動作するか確認します。

$job = Microsoft.PowerShell.ThreadJob\Start-ThreadJob -ScriptBlock {
    1 + 1
}

$result = $job | Wait-Job | Receive-Job
$job | Remove-Job

$result

期待する結果は次のとおりです。

2

実行されたコマンドの提供元を確認する

Get-Command Start-ThreadJob |
    Select-Object Name, Version, Source, ModuleName

SourceまたはModuleNameが次の名前になっていることを確認します。

Microsoft.PowerShell.ThreadJob

旧モジュール修飾名が残っていないか再検索する

$files = Get-ChildItem -Path C:\Scripts `
    -Recurse `
    -File `
    -Include *.ps1, *.psm1, *.psd1

$files | Select-String `
    -Pattern '(?i)(?<![\w.])ThreadJob\\Start-ThreadJob'

何も表示されなければ、直接呼び出し部分の置換は完了しています。

ただし、Import-Module ThreadJob#Requires -Modules ThreadJobは別途確認してください。

実運用と同じアカウントで確認する

対話セッションで成功しても、次の環境では失敗することがあります。

  • タスクスケジューラ
  • Windowsサービス
  • CI/CDエージェント
  • コンテナー
  • Azure Automation
  • 別ユーザーのPowerShellセッション
  • 管理者権限で起動したPowerShell

モジュール検索パスやユーザープロファイルが異なるため、最終確認は実運用と同じユーザー、同じpwsh、同じ作業ディレクトリで行ってください。

確認用のログとして、スクリプトの先頭で次の情報を出力しておくと原因を追いやすくなります。

$PSVersionTable.PSVersion
$PSHOME
$env:PSModulePath

Get-Command Start-ThreadJob -All |
    Select-Object CommandType, Name, Version, Source, ModuleName

旧環境とPowerShell 7.6.4の両方に対応する方法

PowerShell 7.6.4だけを実行対象にできる場合は、新しいモジュール修飾名へ統一するのが最も単純です。

一方、旧PowerShell環境とPowerShell 7.6.4が混在している場合は、次の選択肢があります。

方法向いている環境注意点
新しいモジュール修飾名へ統一PowerShell 7.6.4以降に統一済み旧名しかない環境では失敗する
Start-ThreadJobを未修飾で呼ぶ複数バージョンを簡単に共存させたい自動読み込みや同名コマンドの影響を受ける
利用可能なモジュールを判定する厳密な後方互換性が必要コード量が増える
実行環境へ新モジュールを統一配布する管理された企業環境配布、更新、バージョン管理が必要

未修飾呼び出しを利用する

同名コマンドの競合がなく、モジュール自動読み込みが有効であれば、次の呼び出しが最も簡単です。

Start-ThreadJob -ScriptBlock {
    Get-Date
}

ただし、配布用スクリプトで実行元を厳密に固定したい場合は、モジュール名を判定する方法が適しています。

利用可能なモジュールを判定する

次の例では、新しいモジュールを優先し、見つからなければ旧モジュールを使用します。

$module = Get-Module -ListAvailable `
    -Name Microsoft.PowerShell.ThreadJob |
    Sort-Object Version -Descending |
    Select-Object -First 1

if (-not $module) {
    $module = Get-Module -ListAvailable -Name ThreadJob |
        Sort-Object Version -Descending |
        Select-Object -First 1
}

if (-not $module) {
    throw 'Start-ThreadJobを提供するモジュールが見つかりません。'
}

Import-Module $module -ErrorAction Stop

$startThreadJob = Get-Command `
    -Name Start-ThreadJob `
    -Module $module.Name `
    -ErrorAction Stop

$job = & $startThreadJob -ScriptBlock {
    Get-Date
}

$job | Wait-Job | Receive-Job
$job | Remove-Job

この方法では、単純な未修飾呼び出しよりも実際に取得したコマンドを明確に指定できます。

ただし、組織内でPowerShellのバージョンを統一できるなら、互換分岐を長期間残すより、PowerShell 7.6.4以降と新モジュール名へ統一した方が保守しやすくなります。

Start-Jobへの置き換えは同じ修正ではない

エラーを回避するために、次のようにStart-ThreadJobStart-Jobへ置き換えるのは、単なる名称変更ではありません。

Start-Job -ScriptBlock {
    Get-Date
}

Start-Jobは通常、別プロセスでバックグラウンドジョブを実行します。一方、Start-ThreadJobは同一プロセス内の別スレッドで処理を実行します。

スレッドジョブはプロセス作成のオーバーヘッドを抑えられる一方、プロセス分離が弱く、1つのスレッドジョブがプロセス全体へ影響すると、同一プロセス内の他のスレッドジョブにも影響する可能性があります。([PowerShell ギャラリー][4])

そのため、今回必要なのは次の置換です。

ThreadJob\Start-ThreadJob
↓
Microsoft.PowerShell.ThreadJob\Start-ThreadJob

次の置換ではありません。

Start-ThreadJob
↓
Start-Job

既存スクリプトがスレッドジョブを選んだ理由を確認せずにStart-Jobへ変更すると、パフォーマンス、オブジェクトの受け渡し、メモリ使用量、障害分離の性質が変わる可能性があります。

よくある修正ミス

新しいモジュール名を二重に付けてしまう

単純な文字列置換を繰り返すと、次の誤った名前になることがあります。

Microsoft.PowerShell.Microsoft.PowerShell.ThreadJob\Start-ThreadJob

一括置換では、旧モジュール名だけに一致する正規表現を使用するか、置換後に差分を確認してください。

旧ThreadJobモジュールを再インストールして終わらせる

旧モジュールを追加すると、古いコードが一時的に動く可能性があります。しかし、端末ごとにモジュール構成が異なる状態を作り、移行漏れを見えにくくします。

PowerShell 7.6.4を標準とする場合は、原則としてコードを新しいモジュール名へ合わせます。

直接呼び出しだけ修正する

次の記述を残すと、コマンド呼び出しを修正してもスクリプトの読み込み段階で失敗する可能性があります。

#Requires -Modules ThreadJob
Import-Module ThreadJob
using module ThreadJob

モジュールマニフェストのRequiredModulesも含めて確認してください。

未修飾呼び出しは必ず同じコマンドを実行すると考える

次の呼び出しは通常動作します。

Start-ThreadJob

ただし、同名の関数やエイリアスが存在する場合、別のコマンドが優先される可能性があります。

確認には-Allを付けます。

Get-Command Start-ThreadJob -All

配布スクリプトや管理用スクリプトでは、実行対象を明確にするため、新しいモジュール修飾名を使う方が適切な場合があります。

PowerShell 7.6.4への更新時に確認すべきチェックリスト

  • $PSVersionTable.PSVersionで実際の実行バージョンを確認する
  • Get-Module -ListAvailableで新旧モジュールの存在を確認する
  • Get-Command Start-ThreadJob -Allで提供元を確認する
  • ThreadJob\Start-ThreadJobを検索する
  • Import-Module ThreadJobを確認する
  • #Requires -Modules ThreadJobを確認する
  • using module ThreadJobを確認する
  • .psd1RequiredModulesを確認する
  • 修正前にGitへコミットするかバックアップする
  • 一括置換後に二重プレフィックスがないか確認する
  • 最小のスレッドジョブで動作確認する
  • タスクスケジューラやCI/CDなど実運用環境でも確認する
  • Start-Jobへ安易に置き換えない

PowerShell 7.6.4でThreadJob\Start-ThreadJobが失敗する場合、最初に行うべき対応は、旧モジュール修飾名をMicrosoft.PowerShell.ThreadJob\Start-ThreadJobへ変更することです。

未修飾のStart-ThreadJobを使っているコードは、通常そのまま動作します。ただし、明示的なインポート、#Requires、モジュールマニフェストには旧名が残っている可能性があります。

まず既存コードを検索し、旧モジュール名を新しい名前へ変更してください。その後、Get-Commandで提供元を確認し、実運用と同じPowerShell 7.6.4環境でスレッドジョブを実行できることを確認すれば、名称変更への対応は完了です。
[4]: https://www.powershellgallery.com/packages/Microsoft.PowerShell.ThreadJob/2.2.0 “
PowerShell Gallery
| Microsoft.PowerShell.ThreadJob 2.2.0

この記事を書いた人

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

コメント

コメントする

目次