PowerShellスクリプト(ps1)を実行しやすくする

(2023/10/11 ページ構成を手直し)

PowerShellスクリプト(ps1)に対して、ファイルをドラッグ&ドロップしてもファイルを認識して実行してくれない。

また、そもそもPowerShellスクリプトを実行しようとしても、下記のエラーで実行ができない。

1PS C:\Users\osakanataro\Documents\powershell> .\powershelltest.ps1
2スクリプトの実行がシステムで無効になっているため、ファイル C:\Users\osakanataro\Documents\powershell\powershelltest.ps1 を読み込めません。詳細については、「get-help about_signing」と入力してヘルプを参照してください。
3発生場所 行:1 文字:25
4+ .\powershelltest.ps1 <<<<
5    + CategoryInfo          : NotSpecified: (:) []、PSSecurityException
6    + FullyQualifiedErrorId : RuntimeException
7 
8PS C:\Users\osakanataro\Documents\powershell>

回避方法として、バッチファイルの中からPowerShellを起動する、というものが知られている。

具体的には以下の様なバッチファイルを作って、バッチファイルをクリックする、というものになる。

1@echo off
2rem PowerShellスクリプトの実行が禁止されている場合に
3rem このバッチファイルを管理者権限で動作させると
4rem PowerShellスクリプトが実行できます。
5 
6powershell -sta -ExecutionPolicy Unrestricted -File %0\..\powershelltest.ps1 %*

この場合、PowerShellスクリプトのファイルと、バッチファイルの2つを同時に配布しなければならない、という問題がある。

これを解消するために、バッチファイルの中にPowerShellスクリプトの記述も含めてしまおう、という技がある。

1@echo off
2powershell -sta -ExecutionPolicy Unrestricted "$s=[scriptblock]::create((gc \"%~f0\"|?{$_.readcount -gt 2})-join\"`n\");&amp;$s" %*&amp;goto:eof
3# こんな感じで書く
4$PSVersionTable
5などPowerShellスクリプトの内容を記載

やってることは、バッチファイルからPowerShellを起動したら、そのPowerShellは、いま起動に使ったバッチファイルの2行目の次からスクリプトを読み出しを開始しバッチファイルの最後まで読み込んだあと、PowerShellスクリプトとして実行を開始する、というもの

これによって、バッチファイル1個だけでPowerShellを実行できるようになる。

ただ、この技を使うとLinux/MacOSXなどの他OSのPowerShell環境で動作させにくくなる、という弊害もあるので、Windows環境のみで使う場合にとどめておくと良い。

別解として下記もあると教えていただきました。

1@ set args=%*
2@ powershell "iex( (@('','','')+(cat '%~f0'|select -skip 3))-join[char]10)"
3@ exit /b %ERRORLEVEL%

ここから下は以前の記述


回避方法は下記の2つを行う、ということ

・バッチファイル経由でPowerShellを起動する
・うまく行かない場合は、管理者権限でバッチファイルを起動する

今回使用したバッチファイルは下記の内容とした

1@echo off
2rem PowerShellスクリプトの実行が禁止されている場合に
3rem このバッチファイルを管理者権限で動作させると
4rem PowerShellスクリプトが実行できます。
5 
6powershell -sta -ExecutionPolicy Unrestricted -File %0\..\powershelltest.ps1 %*

なお、powershell起動時に「-sta」オプションをつけているのは、powershellからフォームダイアログを開こうとすると、下記のエラーがでてしまうことを回避するためです。

なお、詳細を開くと下記の情報が出力されている

1Just-In-Time (JIT) デバッグを呼び出すための詳細については、
2ダイアログ ボックスではなく、このメッセージの最後を参照してください。
3 
4************** 例外テキスト **************
5System.InvalidOperationException: DragDrop 登録は成功しませんでした。 ---> System.Threading.ThreadStateException: OLE が呼び出される前に、現在のスレッドが Single Thread Apartment (STA) モードに設定されていなければなりません。Main 関数に STAThreadAttribute が設定されていることを確認してください。
6   場所 System.Windows.Forms.Control.SetAcceptDrops(Boolean accept)
7   --- 内部例外スタック トレースの終わり ---
8   場所 System.Windows.Forms.Control.SetAcceptDrops(Boolean accept)
9   場所 System.Windows.Forms.Control.OnHandleCreated(EventArgs e)
10   場所 System.Windows.Forms.ListBox.OnHandleCreated(EventArgs e)
11   場所 System.Windows.Forms.Control.WmCreate(Message&amp; m)
12   場所 System.Windows.Forms.Control.WndProc(Message&amp; m)
13   場所 System.Windows.Forms.ListBox.WndProc(Message&amp; m)
14   場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message&amp; m)
15   場所 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
16 
17 
18************** 読み込まれたアセンブリ **************
19mscorlib
20    アセンブリ バージョン: 2.0.0.0
21    Win32 バージョン: 2.0.50727.5485 (Win7SP1GDR.050727-5400)
23----------------------------------------
24Microsoft.PowerShell.ConsoleHost
25    アセンブリ バージョン: 1.0.0.0
26    Win32 バージョン: 6.1.7600.16385
28----------------------------------------
29System
30    アセンブリ バージョン: 2.0.0.0
31    Win32 バージョン: 2.0.50727.8686 (QFE.050727-8600)
33----------------------------------------
34System.Management.Automation
35    アセンブリ バージョン: 1.0.0.0
36    Win32 バージョン: 6.1.7601.17514
38----------------------------------------
39Microsoft.PowerShell.Commands.Diagnostics
40    アセンブリ バージョン: 1.0.0.0
41    Win32 バージョン: 6.1.7601.17514
43----------------------------------------
44System.Core
45    アセンブリ バージョン: 3.5.0.0
46    Win32 バージョン: 3.5.30729.5420 built by: Win7SP1
48----------------------------------------
49System.Configuration.Install
50    アセンブリ バージョン: 2.0.0.0
51    Win32 バージョン: 2.0.50727.5483 (Win7SP1GDR.050727-5400)
53----------------------------------------
54Microsoft.WSMan.Management
55    アセンブリ バージョン: 1.0.0.0
56    Win32 バージョン: 6.1.7601.17514
58----------------------------------------
59System.Transactions
60    アセンブリ バージョン: 2.0.0.0
61    Win32 バージョン: 2.0.50727.5483 (Win7SP1GDR.050727-5400)
63----------------------------------------
64Microsoft.PowerShell.Commands.Utility
65    アセンブリ バージョン: 1.0.0.0
66    Win32 バージョン: 6.1.7601.17514
68----------------------------------------
69Microsoft.PowerShell.Commands.Management
70    アセンブリ バージョン: 1.0.0.0
71    Win32 バージョン: 6.1.7601.17514
73----------------------------------------
74Microsoft.PowerShell.Security
75    アセンブリ バージョン: 1.0.0.0
76    Win32 バージョン: 6.1.7601.17514
78----------------------------------------
79Microsoft.PowerShell.ConsoleHost.resources
80    アセンブリ バージョン: 1.0.0.0
81    Win32 バージョン: 6.1.7600.16385
83----------------------------------------
84System.Xml
85    アセンブリ バージョン: 2.0.0.0
86    Win32 バージョン: 2.0.50727.5494 (Win7SP1GDR.050727-5400)
88----------------------------------------
89System.Management
90    アセンブリ バージョン: 2.0.0.0
91    Win32 バージョン: 2.0.50727.5483 (Win7SP1GDR.050727-5400)
93----------------------------------------
94System.DirectoryServices
95    アセンブリ バージョン: 2.0.0.0
96    Win32 バージョン: 2.0.50727.5483 (Win7SP1GDR.050727-5400)
98----------------------------------------
99System.Management.Automation.resources
100    アセンブリ バージョン: 1.0.0.0
101    Win32 バージョン: 6.1.7600.16385
103----------------------------------------
104Microsoft.WSMan.Management.resources
105    アセンブリ バージョン: 1.0.0.0
106    Win32 バージョン: 6.1.7601.17514
108----------------------------------------
109mscorlib.resources
110    アセンブリ バージョン: 2.0.0.0
111    Win32 バージョン: 2.0.50727.5485 (Win7SP1GDR.050727-5400)
113----------------------------------------
114Microsoft.PowerShell.Security.resources
115    アセンブリ バージョン: 1.0.0.0
116    Win32 バージョン: 6.1.7601.17514
118----------------------------------------
119System.Data
120    アセンブリ バージョン: 2.0.0.0
121    Win32 バージョン: 2.0.50727.8692 (QFE.050727-8600)
123----------------------------------------
124System.Windows.Forms
125    アセンブリ バージョン: 2.0.0.0
126    Win32 バージョン: 2.0.50727.5491 (Win7SP1GDR.050727-5400)
128----------------------------------------
129System.Drawing
130    アセンブリ バージョン: 2.0.0.0
131    Win32 バージョン: 2.0.50727.5495 (Win7SP1GDR.050727-5400)
133----------------------------------------
134Accessibility
135    アセンブリ バージョン: 2.0.0.0
136    Win32 バージョン: 2.0.50727.5483 (Win7SP1GDR.050727-5400)
138----------------------------------------
139System.Windows.Forms.resources
140    アセンブリ バージョン: 2.0.0.0
141    Win32 バージョン: 2.0.50727.5420 (Win7SP1.050727-5400)
143----------------------------------------
144 
145************** JIT デバッグ **************
146Just-In-Time (JIT) デバッグを有効にするには、このアプリケーション、
147またはコンピュータ (machine.config) の構成ファイルの jitDebugging
148値を system.windows.forms セクションで設定しなければなりません。
149アプリケーションはまた、デバッグを有効にしてコンパイルされなければ
150なりません。
151 
152例:
153 
154&lt;configuration>
155    &lt;system.windows.forms jitDebugging="true" />
156&lt;/configuration>
157 
158JIT デバッグが有効なときは、このダイアログ ボックスで処理するよりも、
159ハンドルされていない例外はすべてコンピュータに登録された
160JIT デバッガに設定されなければなりません。

2021/05/12追記

これをさらにすすめて、バッチファイル内にPowerShellスクリプトを書いてしまう、という技もあることを知った。

1powershell -sta -ExecutionPolicy Unrestricted "$s=[scriptblock]::create((gc \"%~f0\"|?{$_.readcount -gt 1})-join\"`n\");&amp;$s" %*&amp;goto:eof
2# こんな感じで書く
3$PSVersionTable
4

注意点として、powershellを起動するより前に日本語を書いてはいけない、ということ

これはDOSプロンプトとPowerShellとで日本語の取り扱いが異なるため発生しているので、DOS側では日本語を取り扱わず、PowerShellが起動した後には使う、というようにしなければならない

また、@echo offを入れたい、という場合は下記の様にreadcount の後の数字を増やして対応する。(指定行数より後をPowerShellスクリプトとして読み込み、という命令)

1@echo off
2powershell -sta -ExecutionPolicy Unrestricted "$s=[scriptblock]::create((gc \"%~f0\"|?{$_.readcount -gt 2})-join\"`n\");&amp;$s" %*&amp;goto:eof
3# こんな感じで書く
4$PSVersionTable
5

指定したデータストアがローカルディスクなのかを判定するPowerCLI

PowerShell/PowerCLIを使ってデータストアの空き容量チェックを行う過程で、ローカルディスクのVMFSは無視したい、というものがある。
ESXiホスト台数が増加することを考えると名前による除外などはやりたくない。
何らかの手法でローカルディスクかどうかを判定できないかを探した。

・Get-Datastoreで取得できる情報の範囲で確定できるものが無い
  vSphere6であれば「ExtensionData.Summary.MultipleHostAccess」というデータストアが
  複数のホストからアクセス可能か?というフラグ(true or false)があるが、
  1台のホストだけ接続しているiSCSi,FCデータストアも対象になるのでは?(未確認)
・Get-ScsiLunには「IsLocal」というローカルディスク判定フラグがある
・Get-ScsiLunはデータストアを指定して実行することができる
・ESXiホストが停止しているデータストアに対するGet-ScsiLunはエラーとなる
・アクセス出来ないデータストアは「Accessible」フラグがfalseとなる

これらを元に作成した判定

1$localdatastore=$false # フラグ
2$datastore = Get-Datastore "データストア名"
3if($datastore.Type -eq "VMFS"){
4  # VMFSだとローカルディスクの可能性があり
5  # それ以外の、NFS,vsanは対象外
6  if($datastore.ExtensionData.Summary.MultipleHostAccess -eq $true){
7    # MultipleHostAccessがTrueなら、ローカルディスクではないことが確定
8    $localdatastore=$false
9  }elseif($datastore.Accessible -eq $true){
10    # アクセスできるならGet-ScsiLunがエラーにならない
11    $diskinquire = Get-ScsiLun -Datastore $datastore
12    if($diskinquire.IsLocal -eq $true){
13      # IsLocalによるローカルディスク確定
14      $localdatastore=$true
15    }
16  }else{
17    # アクセスできないデータストア
18  }
19}

PowerShellのファイル出力先を実行ユーザのデスクトップにする

PowerShellのファイル出力先を、実行したユーザのデスクトップにする場合に、「$env:USERPROFILE + “\Desktop\filename.txt”」と指定するのは間違い。

これだと、ローカルディスクでは無く、ファイルサーバ上にDesktopが置かれるような環境に対応できない。

「[Environment]::GetFolderPath(“Desktop”)」で取得するのが正しい。

実際に、どういう風になるのか見てみる・・・

1PS C:\Users\osakanataro> $env:USERPROFILE + "\Desktop"
2C:\Users\osakanataro\Desktop
3 
4PS C:\Users\osakanataro> [Environment]::GetFolderPath("Desktop")
5\\fileserver1\Redirects$\osakanataro\Desktop

Desktop以外に指定できる内容については「Environment.SpecialFolder Enum」(以前のURL Environment.SpecialFolder Enumeration)参照のこと。

「Added in the .NET Framework 4.」なっているものは、Windows7/WindowsServer2008などの古めのWindowsでは動かないので注意。

PowerShellを使って日本語メールを送信する方法(v2.0も対応する版)

PowerShellを使ってメールを送信する方法を探すと2種類でてくる

・PowerShell cmdletの「Send-MailMessage」を使う
・Net.Mail.SmtpClient と Net.Mail.MailMessage を使う

UTF8で日本語メールを送る場合を考えると、Send-MailMessageを使用することで用が足りるようである。

ただ、Windows7で標準インストールとなるPowerShell v2.0だと、「Send-MailMessage -Encoding UTF8 ~」と指定すると、下記のエラーとなり、エンコーディングの指定がうまく行かない。そして、エンコーディングを指定せずに送ると日本語は文字化ける。

1Send-MailMessage : パラメーター 'Encoding' をバインドできません。"eutf8" の値を "System.String" 型から "System.Text.Encoding" 型に変換できません。
2発生場所 行:1 文字:28
3+ Send-MailMessage -Encoding eutf8
4+                            ~~~~~
5    + CategoryInfo          : InvalidArgument: (:) [Send-MailMessage]、ParameterBindingException
6    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SendMailMessage

これは、「UTF8」の代わりに、System.Text.Encodingに変換したUTF8を指定「([System.Text.Encoding]::UTF8)」することで解決できる。

また、PowerShell v2.0のSend-MailMessageには、SMTPポートの番号指定が無い。
25番以外の場合は、「Net.Mail.SmtpClient と Net.Mail.MailMessage」の方を行う必要があるようだ。

そんなわけで、スクリプト例は下記の様になる。
なお、PowerShell Core 6.0でも利用できた。

1$smtpserver= "サーバ名"
2$port= 25
3$mailfrom="ユーザ名@ドメイン名"
4$mailtoArray=@(
5   "ユーザ1@ドメイン名",
6   "ユーザ2@ドメイン名",
7   "ユーザ3@ドメイン名"
8   )
9$mailsubject="メール送信テスト"
10$mailbody="これはメールの送信テストです。`n"+"正常に送信出来ていますか?"
11 
12if($PSVersionTable.PSVersion.Major -gt 2){
13   Send-MailMessage -From $mailfrom -To $mailtoArray -SmtpServer $smtpserver -Port $port -Encoding utf8 -Subject $mailsubject -Body $mailbody
14}else{
15   # PowerShell v2.0用処理。ポート番号指定除去と、Encoding指定方法変更
16   Send-MailMessage -From $mailfrom -To $mailtoArray -SmtpServer $smtpserver -Encoding ([System.Text.Encoding]::UTF8) -Subject $mailsubject -Body $mailbody
17}

え?

ISO-2022-JPでメール送らなくていいのかって?

いまどきUTF-8を受け取れない環境は論外なので気にしなくていいです。(iPhone/Androidの絵文字は基本Unicodeベースなので、送信されるメールはISO-2022-JPではありません)

PowerCLIで対象の仮想マシンを選択してvMotionを実行

VMware vSphere環境で、PowerCLIを使って、仮想マシンのvMotionを行うスクリプトを作成した。

ブラウザを起動し、vSphere Web Clientにログインして、vMotionを実行するまでにかかる時間と
PowerShellを起動して、コマンドを実行する時間を比べると
コマンドを実行したほうがずっと早い、ということで作成した。

というか、vSphere Web Clientは重すぎ!

1# 普通のPowerShell/PowerShell ISEからvSphere PowerCLIのコマンドを使うために必要な設定
2Add-PSSnapin VMware.VimAutomation.Core
3# vCenterサーバに接続
4$VC=Connect-VIServer -User "administrator@vsphere.local" -Password "パスワード" "vCenterサーバ"  -WarningAction 0
5# 仮想マシンの選択
6$VM=Get-VM | Out-GridView -Title "Please select VM" -PassThru
7 
8# 選択した仮想マシンが所属しているクラスタのESXiホストリストから選択
9$ESX=Get-VMHost -Location  ($VM.ResourcePool).Name | Out-GridView -Title "Please select ESXi host" -PassThru
10 
11# 選択したものを表示
12Write-Host "VirtualMachine:" $VM.Name
13Write-Host "Move from:" $VM.VMHost " to:" $ESX.Name
14 
15# vMotionを実行
16Move-VM -VM $VM -Destination $ESX

なお、「$VM=Get-VM | Out-GridView -Title “Please select VM” -PassThru」を
「$VM=Get-VM | Select Name,VMHost | Out-GridView -Title “Please select VM” -PassThru」
という風に替えると、選択ウィンドウに表示する内容を変えることができる。

<注意>
WindowsにインストールされているPowerShellのバージョンが低いと「Out-GridView -PassThru」が利用できないため、動作しません。
たしか、PowerShell ver3以降が必要だったような・・・

StatCounter - Free Web Tracker and Counter