tech.guitarrapc.cóm

Technical updates

PowerShell ModuleのコマンドレットをScriptBlockに格納する

Moduleに含まれるコマンドレットをScriptBlockに入れてごにょごよしたい。そんな事をふと思ってやってみました。

guitarrapc/PowerShellUtil - Convert-ModuleToScriptBlock/Convert-DModuletoScriptBlock.ps1 | GitHub

Moduleのコマンドレットってどういうこと

PowerShell Moduleには1つ以上のコマンドレットが含まれます。 これらをScriptBlockに入れて利用したり出来ないかなーという興味です。

ちなみにRemoteセッション先でモジュールを使えるようにするImport-PSSessionで役に立ちます。初めはこれをやろうと思って考えてたんですけどねー。

powershell - Import-Pssession is not importing cmdlets when used in a custom module - Stack Overflow

コード

モジュールの内容をScriptBlockに埋め込んでみましょう。

$module = "PSWindowsUpdate"
$PSWindowsUpdate = @{}

foreach ( $name in (Get-Command -Module $module).Name)
{
   $cmdlet = $name.Replace("-","")
   $definition = $(Get-Command -module $module | where name -eq $name).Definition
   $PSWindowsUpdate.$cmdlet = [ScriptBlock]::Create($definition)
}

実行するときはスコープに応じて&.を使います。 HashTableに入っているので、モジュール名のプロパティを呼び出すだけにはなっています。

& $PSWindowsUpdate.GetWUList

問題点

paramを渡せないのが問題です。また、COMがリモートセッション超えられません。 リモートセッション先にあるコマンドレットを叩く手も、課題が残って悔しいです。

PowerShellで特定のWindows Updateを検索、削除したい

以前、 Windows Updateの特定のKBを検索を探すやり方を紹介しました。

PowerShellで所定のWindows Updateがインストールされているか確認する

今回は、削除までやってみましょう。 リモート先のサーバーにインストールされたKBも削除できるので、 Windows Updateのバグ対応には便利です。

guitarrapc/PowerShellUtil - Get-KBSearch/Remove-KB.ps1 | GitHub

次の関数を用意しました。

function Remove-KB{
    param(
      [parameter(
      mandatory,
      position = 0)]
      [string[]]
      $kbs
    )

    $PatchList = Get-WmiObject Win32_QuickFixEngineering | where HotFixId -in $kbs

    foreach ($k in $PatchList)
    {
        # If the HotfixID property contains any text, remove it (some do, some don't)
        $KBNumber = $k.HotfixId.Replace("KB", "")

        # Write-Host $KBNumber
        Write-Host ("Removing update with command: " + $RemovalCommand)

        # Build command line for removing the update
        $RemovalCommand = "wusa.exe /uninstall /kb:$KBNumber /quiet /log /norestart"

        # Invoke the command we built above
        Invoke-Expression $RemovalCommand

        # Wait for wusa.exe to finish and exit (wusa.exe actually leverages
        # TrustedInstaller.exe, so you won't see much activity within the wusa process)
        while (@(Get-Process wusa -ErrorAction SilentlyContinue).Count -ne 0)
        {
          Start-Sleep 1
          Write-Host "Waiting for update removal to finish ..."
        }
    }
}

実行するときは、kbを渡します。KBが付いていてもいいです。無くてもいいです。

Remove-KB -kbs "KB2821895"

自動的にWindows Updateを削除してくれるので、纏まった台数の削除をする際にはないと困りますね。

PowerShellでRemote Desktopを起動させる

Remote DesktopはWindowsのリモートセッションでは標準であるうえ性能の向上が著しいため多くのヒトが使っているいることでしょう。 Remote Desktopで毎回IPを入力するのもめんどくさい、あるいはRDP構成ファイルを触るのもやだなどという場合に、PowerShellで下準備をしてあげる方法です。

guitarrapc/PowerShellUtil - Start-RDP/Start-RDP.ps1 | GitHub

RemoteDesktop接続が可能な相手かを確認する

ただ単にRemoteDesktop起動画面を出すのでは面白くないわけで。 そもそも相手がRemote Desktopできる対象か調べるといいのでは的な。

$server = "192.168.1.1"
$RDPPort = 3389
New-Object System.Net.Sockets.TCPClient -ArgumentList $server,$RDPPort

このようにすると接続先がRDPに対応しているかBoolで判定されます。Test-ConnectionなどでIP判定とかもろもろするといいのですが、今回は省きます。

function Start-RDP {
    [CmdletBinding()]
    param(
    [parameter(
        mandatory,
        position = 0)]
    [string]
    $server,

    [parameter(
        mandatory = 0,
        position = 1)]
    [string]
    $RDPPort = 3389
    )

    # Test RemoteDesktop Connection is valid or not
    $TestRemoteDesktop = New-Object System.Net.Sockets.TCPClient -ArgumentList $server,$RDPPort

    # Execute RDP Connection
    if ($TestRemoteDesktop)
    {
        Invoke-Expression "mstsc /v:$server"
    }
    else
    {
        Write-Warning "RemoteDesktop 接続ができませんでした。ネットワーク接続を確認してください。"
    }
}

利用するときはこのような感じで。

Start-RDP -server "ServerIp"