🔀

[PowerShell]6.0以降の環境下で5.1のコマンドレットを実行する方法

2024/03/07に公開

概要

PowerShellでイベントログを出力する場合、Write-EventLogコマンドレットで簡単に実現できましたが、
バージョン6.0のCore Editionから「*-EventLog」関連のコマンドレットが削除されました。

その為、PowerShell 6.0以降では、*-EventLog関連のコマンドレットを実行できません。
そこで今回は、6.0以降のPowerShell Core環境下でもPowerShell 5.1環境のコマンドレットを実行する方法を調べました。

この記事のターゲット

  • PowerShell ユーザーの方
  • PowerShell 6.0以降の環境で5.1のコマンドレットを実行したい方

環境

Windows OS

Windows 10 Pro環境

Get-WmiObjectコマンド
PS C:\Users\"ユーザー名"> Get-CimInstance CIM_OperatingSystem

SystemDirectory     Organization BuildNumber RegisteredUser SerialNumber            Version
---------------     ------------ ----------- -------------- ------------            -------
C:\WINDOWS\system32              19045       XXXXX          00000-00000-00000-AAAAA 10.0.19045
                                             ^^^^^          ^^^^^ ^^^^^ ^^^^^ ^^^^^
                                             ↑マスク       ↑マスク

PS C:\Users\"ユーザー名">

PowerShell

実行元:PowerShell 7.x

PS C:\Users\"ユーザー名"> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.4.1
PSEdition                      Core
GitCommitId                    7.4.1
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

PS C:\Users\"ユーザー名">

実行先:PowerShell 5.x

PS C:\Users\"ユーザー名"> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.4046
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.4046
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\Users\"ユーザー名">

対応方法

全体的な流れをシーケンス図に起こすと、下記のとおり。

以降より下記3つの方法を記載。

A. powershellコマンドで実行

5.1でしか動作しないGet-EventLogコマンドレットを実行

  1. PowerShell Core ウィンドウを起動

    ⊞ Windowsキー + R 」で“ファイル名を指定して実行”を起動し「 pwsh 」と入力しEnter。

  2. 実行するコマンドを変数に代入
    5.1環境のみ動作するGet-EventLogコマンドレットを記述。

    コピー用
    [System.String]$command_text = @"
    Get-Date
    Get-EventLog -LogName 'Application' -EntryType Information -Source 'System Restore'
    "@
    
  3. powershellコマンドにより5.1環境でコマンドを実行

    コピー用
    powershell -Command $command_text
    
実際に実行した結果 < クリックで折りたたみが開く >
実際に実行した結果
PS C:\Users\"ユーザー名"> [System.String]$command_text = @"
>> Get-Date
>> Get-EventLog -LogName 'Application' -EntryType Information -Source 'System Restore'
>> "@
PS C:\Users\"ユーザー名">
PS C:\Users\"ユーザー名"> powershell -Command $command_text

2024年3月7日 10:35:57

MachineName        : "コンピューター名"
Data               : {0, 0, 0, 0...}
Index              : 182058
Category           : (0)
CategoryNumber     : 0
EventID            : 8212
EntryType          : Information
Message            : スケジュールされた復元ポイントは正常に作成されました。
Source             : System Restore
ReplacementStrings : {}
InstanceId         : 8212
TimeGenerated      : 2024/03/06 13:08:59
TimeWritten        : 2024/03/06 13:08:59
UserName           :
Site               :
Container          :


MachineName        : "コンピューター名"
Data               : {0, 0, 0, 0...}
Index              : 182057
Category           : (0)
CategoryNumber     : 0
EventID            : 8194
EntryType          : Information
Message            : 復元ポイントが作成されました (プロセス = C:\WINDOWS\system32\srtasks.exe ExecuteScheduledSPPCreati
                     on; 説明 = スケジュールされたチェックポイント)。
Source             : System Restore
ReplacementStrings : {C:\WINDOWS\system32\srtasks.exe ExecuteScheduledSPPCreation, スケジュールされたチェックポイント}
InstanceId         : 8194
TimeGenerated      : 2024/03/06 13:08:59
TimeWritten        : 2024/03/06 13:08:59
UserName           :
Site               :
Container          :

~~~ 省略 ~~~

PS C:\Users\"ユーザー名">

前述しているとおり、実行元のPowerShell Core ウィンドウ(6.0以降の環境)のコンソールを管理者として実行した場合、
実行先の5.1環境でも管理者権限がある状態で実行されます。

5.1でしか動作しないNew-EventLogコマンドレットを実行

下記は管理者権限が必要なコマンドレットNew-EventLogを一般権限と管理者権限で実行してみました。

一般権限で実行するとエラー
PS C:\Users\"ユーザー名"> [System.String]$command_text = @"
>> Get-Date
>> New-EventLog -LogName Application -Source MyAppSource
>> "@
PS C:\Users\"ユーザー名">
PS C:\Users\"ユーザー名"> powershell -Command $command_text

2024年3月6日 16:30:43
New-EventLog : アクセスが拒否されました。昇格されたユーザー権限 (つまり、[管理者として実行]) を使用して開かれたセッショ
ンでコマンドを再実行してください。
発生場所 行:2 文字:1
+ New-EventLog -LogName Application -Source MyAppSource
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-EventLog]、Exception
    + FullyQualifiedErrorId : AccessIsDenied,Microsoft.PowerShell.Commands.NewEventLogCommand



PS C:\Users\"ユーザー名">

上記の場合、管理者権限がなかったので異常終了。

管理者権限で実行すると正常終了
PS C:\Users\"ユーザー名"> [System.String]$command_text = @"
>> Get-Date
>> New-EventLog -LogName Application -Source MyAppSource
>> "@
PS C:\Users\"ユーザー名">
PS C:\Users\"ユーザー名"> powershell -Command $command_text

2024年3月6日 16:31:19


PS C:\Users\"ユーザー名">

上記は、管理者権限があり問題なく5.1環境で実行できた。

補足情報:powershellコマンドの実行ファイルの場所 < クリックで折りたたみが開く >

Get-Command(gcm)で確認可能。
私の環境では「C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe」だった。

実際に実行した結果
PS C:\Users\"ユーザー名"> gcm powershell

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Application     powershell.exe                                     10.0.1904… C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe

PS C:\Users\"ユーザー名">

B. 一般権限の状態でコマンド実行時に管理者として実行

Start-Processコマンドレット で 5.1の実行ファイル powershell.exe を呼び出す方法を実行します。
なお、コマンド実行時に管理者権限として実行する方法は、Start-Processコマンドレットのオプション Verb RunAs を指定する事により実現。

  1. PowerShell Core ウィンドウを一般権限で起動
    ⊞ Windowsキー + R 」で“ファイル名を指定して実行”を起動し「 pwsh 」と入力しEnter。

  2. 実行するコマンドを変数に代入

    実行するコマンドを変数に代入
    $command_text = @"
    -Command 
    Get-Date > D:\Downloads\output.txt
    Remove-EventLog -Source MyAppSource >> D:\Downloads\output.txt
    "@
    
  3. Start-Process経由で管理者として実行

    5.1環境では管理者として実行
    Start-Process -Verb RunAs -FilePath powershell.exe -ArgumentList $command_text
    
  4. 実行した結果を表示

    実行した結果を表示
    Get-Content D:\Downloads\output.txt
    
実際に実行した結果 < クリックで折りたたみが開く >
実際に実行した結果
PS C:\Users\"ユーザー名"> $command_text = @"
>> -Command
>> Get-Date > D:\Downloads\output.txt
>> Remove-EventLog -Source MyAppSource >> D:\Downloads\output.txt
>> "@
PS C:\Users\"ユーザー名">
PS C:\Users\"ユーザー名"> Start-Process -Verb RunAs -FilePath powershell.exe -ArgumentList $command_text
PS C:\Users\"ユーザー名">
PS C:\Users\"ユーザー名"> Get-Content D:\Downloads\output.txt

2024年3月7日 10:45:08


PS C:\Users\"ユーザー名">

C. dotNET Frameworkで実行

PowerShell Core ウィンドウを起動するまでは、「A. powershellコマンドで実行」と同じ手順。
その後、下記を実行することで.NET Frameworkのコードを実行できます。

なお、管理者権限が必要なコマンドレットを実行したい場合、「A. powershellコマンドで実行」と同様、
PowerShell Core ウィンドウを起動する際に“管理者として実行”で立ち上げることで実現可能。

.NET Frameworkで実行
$ps_setdata = New-Object System.Diagnostics.ProcessStartInfo
$ps_setdata.FileName = "powershell"
$ps_setdata.RedirectStandardError = $true
$ps_setdata.RedirectStandardOutput = $true
$ps_setdata.UseShellExecute = $false
$ps_setdata.Arguments = @"
Get-Date
New-EventLog -LogName 'Application' -Source 'MyAppSource'
Get-EventLog -LogName 'Application' -EntryType Error -Source 'System Restore'
"@
$ps = New-Object System.Diagnostics.Process
$ps.StartInfo = $ps_setdata
$ps.Start() > $null
$ps.WaitForExit()
$ps_output = $ps.StandardOutput.ReadToEnd()
$ps_error = $ps.StandardError.ReadToEnd()
Write-Host ''
Write-Host ''
Write-Host 'Standard Output --- Start ---'
Write-Host "$ps_output"
Write-Host 'Standard Output ---  End  ---'
Write-Host ''
Write-Host ''
Write-Host 'Error Output --- Start ---'
Write-Host "$ps_error"
Write-Host 'Error Output ---  End  ---'
Write-Host ''
Write-Host ''
Write-Host "Exit Code: [$($ps.ExitCode)]"
Write-Host ''
実際に実行した結果 < クリックで折りたたみが開く >
実際に実行した結果
PS C:\Users\"ユーザー名"> $ps_setdata = New-Object System.Diagnostics.ProcessStartInfo
>> $ps_setdata.FileName = "powershell"
>> $ps_setdata.RedirectStandardError = $true
>> $ps_setdata.RedirectStandardOutput = $true
>> $ps_setdata.UseShellExecute = $false
>> $ps_setdata.Arguments = @"
>> Get-Date
>> New-EventLog -LogName 'Application' -Source 'MyAppSource'
>> Get-EventLog -LogName 'Application' -EntryType Error -Source 'System Restore'
>> "@
>> $ps = New-Object System.Diagnostics.Process
>> $ps.StartInfo = $ps_setdata
>> $ps.Start() > $null
>> $ps.WaitForExit()
>> $ps_output = $ps.StandardOutput.ReadToEnd()
>> $ps_error = $ps.StandardError.ReadToEnd()
>> Write-Host ''
>> Write-Host ''
>> Write-Host 'Standard Output --- Start ---'
>> Write-Host "$ps_output"
>> Write-Host 'Standard Output ---  End  ---'
>> Write-Host ''
>> Write-Host ''
>> Write-Host 'Error Output --- Start ---'
>> Write-Host "$ps_error"
>> Write-Host 'Error Output ---  End  ---'
>> Write-Host ''
>> Write-Host ''
>> Write-Host "Exit Code: [$($ps.ExitCode)]"
>> Write-Host ''


Standard Output --- Start ---

2024年3月7日 11:12:28



Standard Output ---  End  ---


Error Output --- Start ---
Get-EventLog : 一致する項目が見つかりません
発生場所 行:2 文字:1
+ Get-EventLog -LogName 'Application' -EntryType Error -Source 'System  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (:) [Get-EventLog], ArgumentException
    + FullyQualifiedErrorId : GetEventLogNoEntriesFound,Microsoft.PowerShell.Commands.GetEventLogCommand


Error Output ---  End  ---


Exit Code: [1]

PS C:\Users\"ユーザー名">

参考情報

まとめ

それぞれの実行方法を試した結果、

  • A. powershellコマンドで実行
    一番対応しやすい方法。ちょっとした5.1環境のコマンドを実行するのであればオススメ。
  • B. 一般権限の状態でコマンド実行時に管理者として実行
    ちょっとクセがある方法だが、ログインユーザーが一般ユーザーだった場合に管理者権限の付与を最小限にできるかも。
  • C. dotNET Frameworkで実行
    C#のコードを使用できるので、実施できる範囲が広い。ただ、処理速度は遅いのがネック。

という感触でした。

関連記事

https://haretokidoki-blog.com/pasocon_powershell-startup/
https://zenn.dev/haretokidoki/articles/7e6924ff0cc960

GitHubで編集を提案

Discussion