目次
よくいるコピペSEだけども、よく使う/便利だったコマンドやスクリプトをまとめて置いておきたいと思いこのブログを作りました。
本来ならqiitaとかでも良いんじゃないかと思うものの、専門的か、とか技術的かどうかとか言われると心苦しいのでとりあえずはここで。
(順番を新しい順にしました)
<掲載済>
【MySQL】SELECTで「行と列を入れ替える」方法(クロス集計・ピボット風) - うまく動けばいいな!
【bat】ブロックされるようになったネットワーク上のExcelのVBAマクロを、batで実行可能にする(regコマンド、信頼済みサイト、Excelファイル) - うまく動けばいいな!
【JavaScript】一定期間の年月(YYYY/MM)の配列を作る([2021/04,2021/05,2021/06]とか。moment.jsを利用) - うまく動けばいいな!
【Powershell】Thunderbirdの「about:config」を簡易的に変更するCUIツールを作る(あるファイルに追記するPowershellスクリプト) - うまく動けばいいな!
【Powershell】(サーバ保守)サービス登録しないでログオン失敗を検知しメール送信する仕組みをつくる - うまく動けばいいな!
【Powershell】(Tips集)PowerShellよく使うコードの紹介 - うまく動けばいいな!
【Powershell】(サーバ保守)毎日定期的に作られるフォルダを週次で古いものを圧縮処理する - うまく動けばいいな!
【Powershell】(サーバ保守)タスクスケジューラの特定のタスクの状態を確認する(Get-ScheduledTask) - うまく動けばいいな!
【bat、Powershell】(PCメンテナンス用)デスクトップにショートカットを作成するスクリプト - うまく動けばいいな!
【PHP】PHPをbatのようにローカルスクリプトとして使う(3)~Curl編(Webアクセス、外部API(HTTP)の利用、JSONデータの活用)~ - うまく動けばいいな!
【PHP】PHPをbatのようにローカルスクリプトとして使う(2)~DBアクセス編~ - うまく動けばいいな!
【bat、PHP】PHPをbatのようにローカルスクリプトとして使う(1)~設定・呼び出し編~ - うまく動けばいいな!
【bat】(ある程度)セキュアなファイル転送を自動で行うbat ~WinSCP公開鍵転送の自動化~ - うまく動けばいいな!
【Powershell】(サーバ保守)PingをPowershellでリスト化した端末へ実行する(Test-Connection) - うまく動けばいいな!
【Powershell】(サーバ保守)フォルダ内ファイル内キーワード検索+圧縮+メール送信 - うまく動けばいいな!
【PowerShell】(サーバ保守)イベントログのログオン履歴抽出②(Get-WinEvent) - うまく動けばいいな!
【bat、Powershell】(サーバ保守)ディスク容量監視、メール送信 - うまく動けばいいな!
【PowerShell】(PCメンテナンス用)メール送信② - うまく動けばいいな!
【PowerShell】(サーバ保守)イベントログのログオン履歴抽出、CSV化(Get-WinEvent) - うまく動けばいいな!
【コマンド(bat)】PSEXECを利用したリモート管理 - うまく動けばいいな!
【PowerShell】(PCメンテナンス用)メール送信 - うまく動けばいいな!
【bat、PowerShell】(PCメンテナンス用)PC設定まとめて取得 - うまく動けばいいな!
【bat】(PCメンテナンス用)ディスククリーンアップ - うまく動けばいいな!
【bat】(PCメンテナンス用)システムファイルチェッカー、DISMCleanup-Image - うまく動けばいいな!
【bat】(個人用ツール)NET DRIVEのbat - うまく動けばいいな!
<予定>
未定(ネタなんかあったっけな)
本ブログに掲載している情報やソースコードの正確性は注意を払っていますが、
情報・ソースコード等により生じるいかなる損失・損害に対しては、一切の責任を負いかねます。
【MySQL】SELECTで「行と列を入れ替える」方法(クロス集計・ピボット風)
【ツールの概要】
SQLで行と列を入れ替えて更新したい場面、よくありますよね。。
業務システムなどで特によくあるイメージ。ピボット処理って言うのかな?
こんな時に使うかも?
・社内の売上レポートや購買集計で「商品ごとに列にしたい」
・Excelに出力する前に整った形でSQL結果を取得したい
・BIツールに渡す中間データとして使いたい
【環境(使っている環境)】
・MySQL 5.7 / 8.0
・Windows 10 Pro(クライアント側)
・テーブルは以下みたいな感じ

●1 最大値を取るSQLクエリ
-- 各item_name についてcustomer_nameごとの最大値を取る SELECT customer_name, MAX(CASE WHEN item_name = 'りんご' THEN quantity END) AS りんごの数, MAX(CASE WHEN item_name = 'みかん' THEN quantity END) AS みかんの数, MAX(CASE WHEN item_name = 'バナナ' THEN quantity END) AS バナナの数 FROM test_table GROUP BY customer_name;
【説明】
・各レコードを列行で変換しながら、customer_nameごとの最大値をとる。
・1行目:顧客ごとに集計するため、GROUP BYにcustomer_nameを指定。
・2~4行目:各商品をCASE式で分岐させ、それぞれ列として表示。値はquantityをそのまま使い、NULLを避けるためMAX()で包んでいます(1人1商品につき1行である前提)。
【結果サンプル】

●2 合計を取るSQLクエリ
-- 各item_name についてcustomer_nameごとの合計値を取る SELECT customer_name, SUM(CASE WHEN item_name = 'りんご' THEN quantity ELSE 0 END) AS りんごの合計, SUM(CASE WHEN item_name = 'みかん' THEN quantity ELSE 0 END) AS みかんの合計, SUM(CASE WHEN item_name = 'バナナ' THEN quantity ELSE 0 END) AS バナナの合計 FROM test_table GROUP BY customer_name;
【説明】
・各レコードを列行で変換しながら、customer_nameごとの合計値をとる。
・1行目:customer_nameごとに集計するため、GROUP BY customer_nameを使用します。
・2~4行目:CASE文を使って、item_nameごとにquantityを振り分け。
- 該当する商品名の場合にquantityを取り出し、該当しない場合は0を返します。
- これにより、NULLにならずに0埋めされた集計表ができます。
・各商品の合計をSUM()で計算することで、**「各顧客が、商品をいくつ買ったかの合計表」**が作れます。
【結果サンプル】

【補足】
・クエリのなかで、列(アイテム名)は固定で指定する必要があります
→アイテムの種類が頻繁に増減する場合は、動的SQL(アプリ側でSQL生成)やビューの組み合わせが必要になるかもしれません
・下記のようなプロシージャでCALLするのも悪くはないかも?
-- ストアドプロシージャはオプション(1回実行でOK) SET @sql = NULL; -- 1. 商品名(item_name)の一覧を動的に取得して、SUM(CASE ...) を組み立てる SELECT GROUP_CONCAT( DISTINCT CONCAT( 'SUM(CASE WHEN item_name = ''', item_name, ''' THEN quantity ELSE 0 END) AS `', item_name, '`' ) ) INTO @sql FROM test_table; -- 2. 完全なSQLを構築(customer_nameごとにGROUP BY) SET @sql = CONCAT('SELECT customer_name, ', @sql, ' FROM test_table GROUP BY customer_name'); -- 3. 動的SQLとして実行 PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
【総評】
とりあえず場当たり的に作りましたが、
「縦持ちデータを見やすく整理したい」という場面では即戦力になります!
複雑な商品カテゴリや月別売上、動的列名対応などは別途工夫が必要ですが、基本をおさえておけば応用もスムーズです。
【bat】ブロックされるようになったネットワーク上のExcelのVBAマクロを、batで実行可能にする(regコマンド、信頼済みサイト、Excelファイル)
【ツールの概要】
はてなから見てた、これ対策
togetter.com
・ネットワーク上のOfficeファイルのVBAマクロについて、デフォルトでブロックされるようになりました。(赤い警告が出る)
・各PCの「インターネットオプション」-「信頼済みサイト」に各ネットワークのサーバー(FQDN、アドレス)を入力すればマクロ実行可能になるとのこと。
(他にも方法はありますが)
・その設定をbatファイルでやってみる!
・レジストリをいじるので、慎重に!自己責任で!regedit.exeも確認しながら!
・MSの参考サイトはこちら↓
Macros from the internet are blocked by default in Office - Microsoft 366 Apps | Microsoft Learn
・その他参考サイトはこちら
IEの信頼済みサイトを一括で登録 | 社内SE3割増し
バッチファイルでIEの信頼済みサイトを登録 ← RootLinks Co., Ltd.
IE11のローカルイントラネットゾーンの設定をするレジストリ - QWERTY.WORK
【環境(使っている環境)】
・Windows 10 Pro (ビルド19044.1889)
【ソース(AddTrustedSites.bat)】
@reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Ranges\Range1" @IF %errorlevel%==0 goto exists @REM 値作成 reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Ranges\Range1" /v file /t REG_DWORD /d 2 /f reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Ranges\Range1" /v :Range /t REG_SZ /d 192.168.0.XX /f @REM 値作成 reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\SERVERXX\\" /v file /t REG_DWORD /d 2 /f @echo. @echo 処理が完了しました。 exit /b :exists @echo. @echo エラー!既に信頼済サイトに何か登録があるので、整合性のためGUIで登録をしてください。 pause
【説明】
・1-2行目。すでに値がレジストリに登録されている場合はエラーのほうへ。
判定も割に合わないので、諦めてGUIでやりましょう!←
・3-5行目。IPアドレスをRangeとして登録します。Excelファイルを実行するためなので、プロトコルはfile:です。
・6-7行目。サーバのFQDNホスト名をDomainsの値として登録します。
・8行目以降。処理結果で画面出力を変更しています。エラーの場合はpauseで残ります。
【総評】
とりあえず場当たり的に作りました。
レジストリの変更なので検証を慎重に行ってくださいね!
これで出来ない場合は、インターネットオプションをいじるようにすればよいかと・・・
【Powershell】「実行した」くらいのログを取りながら、共有フォルダ内のファイルのセッション切断をする(Start-Transcript、Close-SmbOpenFile)
【ツールの概要】
・該当のフォルダ内の「.pdf」の拡張子のセッションを切断する。
・標準出力(かな?エラー出力も出るけど全部かわかりません)をログとして記録する。
Linuxとかの「tee」コマンドと似たような感じで記録する。
【環境(使っている環境)】
・Windows Server 2012 R2
・Powershell ver4.0
・実行には管理者権限が必要です
【ソース(closesmb.ps1)】
$Today=(Get-Date).ToString("yyyyMMdd_HHmmss") Start-Transcript -path "D:\Project\CloseSmbFile\log\transcript-$($Today).txt" -Append Write-Output "切断対象リスト" Get-SmbOpenFile | Where-Object -Property Path -Match "E:\pdfホルダー" | Where-Object -Property Path -Match ".pdf"| %{echo $_} | Format-List Write-Output "切断します" Get-SmbOpenFile | Where-Object -Property Path -Match "E:\pdfホルダー" | Where-Object -Property Path -Match ".pdf"| Close-SmbOpenFile -force Write-Output "切断完了しました" Stop-Transcript
【説明】
・1行目はlogファイル名用の時間取得です。
・2行目、Start-Transcript でその後のコマンドの出力をlogファイルに"も"出力します。-Append パラメータで追記を許可します(秒単位で別のlogファイルにはなりますが)。
・3、5,7行目、Write-Output は標準出力としてlogにも記録されます。Write-Hostでも同様に・・・(のはず)。
・4行目、Get-SmbOpesnFile で「開いているファイル」の一覧が呼べます。パイプで条件を指定し、Format-List で出力します。
・6行目、再度Get-SmbOpesnFile の結果から同じ条件で出力された一覧のそれぞれの要素(ファイル)に、Close-SmbOpenFileを実行します。
・8行目、Stop-Transcript でのlog出力を終了します。
・ちょっと冗長な記述ですが、わかりやすく、ということで。。
【総評】
log専用に関数を作ったりするほどじゃないな、ってときとかに便利ですね、Start-Transcript。
実運用はもっと複雑な条件だったりすると思うので、ここまで小さいスクリプトじゃないと思いますが、
ちょろっと作るけど一応logでも記録したいなって時にはよいですね、実行バージョンとかも出ますし。
【JavaScript】一定期間の年月(YYYY/MM)の配列を作る([2021/04,2021/05,2021/06]とか。moment.jsを利用)
【ツールの概要】
・['YYYY/MM', 'YYYY/MM', ...]な感じで月単位の配列を作る。
・moment.jsを活用する。
・今回は自分の都合上、3年前の最初(1月)から当月までの年月文字列配列を作ります。
【環境(使っている環境)】
・moment.js(2.29.1)
【ソース(関数部分)】
let arr = []; let i = 0; let start = moment().add(-3, 'years').startOf('year'); // addせず、moment(なにか)で開始日を指定するのもいいですね let end = moment().startOf('month'); // 今月までなのでmoment()です let diffmonthes = end.diff(start, 'months'); // 201801-202105なら40がとれます for(; i <= diffmonthes; i++) { arr.push(start.clone().add(i, 'months').startOf('month').format('YYYY/MM')); } console.log(arr);
【説明】
・moment.jsを読み込んだ状態で実行します。
・momentオブジェクト?(Dateオブジェクトをラッパした感じのもの)はかなり多機能で、javascriptで日付を扱うならmoment.jsをぜひ利用したいもの。
・4、5行目で3年前の1月月初と当月月初のmomentオブジェクトを取得します。
startとendについて、いずれも月初(startOf)としています。
その理由は差分を取る時に同じ日付としてないと差分月数が正確に取れなさそうなためです。
6行目でdiff( , 'month')で差分の月数を取ります。(2021/4/1~2021/5/1なら1になります。日付がずれてて且つ引数3つ目にtrueを指定すると小数までdiffがでます)
Month and year diffs
moment#diff has some special handling for month and year diffs. It is optimized to ensure that two months with the same date are always a whole number apart.
~~
出典:Moment.js Documentation
・あとはfor文で一月ずつcloneしてformatで指定した形式の文字列を配列にaddします。
add(0, 'month')で当月いけるっぽいです
ここのstartOfは不要だと思いますがなんとなく。。
clone()はmoment()でもよさげですね
iをあくまでイテレータとして1ずつstartそのものを加算しても良いですね
【総評】
意外と記事がなかったのでまとめました。
標準のDateオブジェクトだとなかなか遠回りになりそうなので、moment.jsの便利さが際立ちます。
【Powershell】Thunderbirdの「about:config」を簡易的に変更するCUIツールを作る(あるファイルに追記するPowershellスクリプト)
【ツールの概要】
・Thunderbirdというメーラー(フリー版あり)の「オプション」からいじれる「about:config」という設定を変更する。
・「about:config」は以下のような「prefs.js」というJavascriptファイルになっているので、追記する。
・おそらく推奨ではないので、自己責任で実行してください!(あくまでPowershellのお勉強としてお願いします)
・Thunderbird起動中の編集は無意味。また、編集後、起動時に読み取られ、設定がダブった場合は最後の行のみ反映される(順序が整理される)。さらに、デフォルト値の場合、削除される。
・ログインしてるユーザのプロファイル中の「about:config(prefs.js)」をいじる。全部いじる。


【環境(使っている環境)】
・Windows10
・Powershell ver5.1
・Thunderbird v78.4.0
【ソース(CallPS1.bat、呼び出し用bat)】
@echo off cd /d %~dp0 SET PSFile=ChangeThunderBirdPrefs.ps1 SET ThunderbirdApplicationName="thunderbird.exe" echo 処理を開始します。 echo. REM Thunderbird 実行確認。実行してたら終了。 tasklist | find %ThunderbirdApplicationName% if %ERRORLEVEL% == 0 ( echo Thunderbird が実行中です。Thunderbird を終了し、再度実行してください。 echo. exit /b ) REM ps1ファイル存在確認。存在してなかったら終了。 if not exist %PSFile% ( echo ChangeThunderBirdPrefs.ps1をこのファイルと同じ階層に置いて再度実行してください。 echo. exit /b ) REM ps1ファイル実行。 powershell -ExecutionPolicy RemoteSigned .\%PSFile% if %ERRORLEVEL% == 1 ( echo 処理に失敗しました。 echo. exit /b ) echo 処理が正常終了しました。 echo. pause
【ソース(ChangeThunderBirdPrefs.ps1、特定ファイルへの追記処理他もろもろ)】
$ErrorActionPreference = "Stop" # prefs.jsは既定通りインストールしていれば下記のような場所にあります。「~default」フォルダは複数ある可能性もあり。 # $env:APPDATA + "\Thunderbird\Profiles\XXXXXXX.default\prefs.js"; # ということでProfilesフォルダをまず取得 $ProfilesFoldersPath=$env:APPDATA + "\Thunderbird\Profiles"; # Profilesフォルダ内のフォルダコレクションを取得したいので、Get-ChildItem。 try{ [System.IO.DirectoryInfo[]] $dirs = Get-ChildItem $ProfilesFoldersPath -Directory; } catch [Exception] { Write-Output ("実行アカウントの既定のフォルダにThunderbirdプロファイルフォルダがありませんでした。"); throw; # code 1 return. } try{ foreach($dir in $dirs){ # 処理するProfilesフォルダ内のフォルダ名をコンソールに表示 Write-Output ('対象フォルダ :' + $dir.FullName); $targetJSFile=$dir.FullName + "\prefs.js"; Write-Output ("対象ファイル :" + $targetJSFile); # prefs.jsの存在チェック if((Test-Path $targetJSFile) -eq $false){ Write-Output ("変更用ファイルが存在しませんでした。thunderbirdを起動し、手動で設定してください。 `r`n" + $targetJSFile); continue; } Write-Output ("ファイルが存在しました。処理を続行します。"); # 追記処理。最後の行だけ設定として生きるよ、thunderbird起動時にきれいになるよ、ということを説明。 # if any same keys exists, only last row parameter is enable.(when thunderbird application executed.) # and... new line , line feed not exiting is no probrem.(thunderbird application is formating the file, after read.) Write-Output ("user_pref(`"security.tls.version.min`", 1);") | Add-Content -Encoding utf8 $targetJSFile; } } catch [Exception] { Write-Output ("設定ファイルの書込み時に何らかのエラーが発生しました。thunderbirdを起動し、手動で設定してください。"); throw; # code 1 return. } Write-Output ("設定追記処理終了しました。"); return 0;
【説明】
・追記している「"user_pref(`"security.tls.version.min`", 1);"」という文字列は、TLSの下限バージョンを1.1まで引き下げるというものです。
(デフォルトでは1.3-1.2までしか使えない)
・batを実行します。batはps1ファイルをRemoteSignedで実行させます。
・batではThunderbirdが実行中かについてプロセスを見て判断します。あったら終了。
あとはps1ファイルが無かったりエラーだったらその旨表示して終了。
・ps1の方では、Profilesフォルダの中に複数のプロファイルがある可能性があるので、全部に処理するためforeach(雑)。
・test-pathでチェックしてるので、try-catchでcatchに行くときは書き込めなかったとき等なので、その場合は処理自体中断し終了。
(1行目でErrorActionPreferenceをStopに設定しているため、このようなエラー時には継続しません。)
【総評】
それぞれの行でやってることが明確に判るよう意識しています。
Thunderbirdがベキ等性を保証してくれるので(絶対そのためじゃないけど)、
何度実行して延々とprefs.jsに追記しても、たぶん、Thunderbirdが実行時に1行にしてくれます!(そのうち仕様が変わる可能性あり)
結構即席で作ったので、try-catchの当たりなどは工夫し甲斐が残ったりしてそうですね。
【Powershell】(サーバ保守)サービス登録しないでログオン失敗を検知しメール送信する仕組みをつくる
【ツールの概要】
・タスクスケジューラ上に特定のWindowsイベントログをトリガーとしたタスクを作る。
・Powershellで、フィルターされた直近数分間のイベントログのデータをメールで送る。
【環境(使っている環境)】
・Windows Server2016(+ Windows10)
・Powershell ver5.1
【ソース(AlertLoginError.ps1、メイン処理)】
# クラスファイルを読み込む using module .\class\Class_CommonFunction.psm1 using module .\class\Class_GetEvents.psm1 using module .\class\Class_Sendmail.psm1 # 各変数をセットアップする、ログの命名などにも使用 $DocRoot = Split-Path (Split-Path $MyInvocation.MyCommand.Path -Parent) -Parent $YMD = Get-Date -Format "yyyyMMdd" $yMdHms = Get-Date -Format "yyyyMMddHHmmss" # xmlにメールの設定を格納しておく $mailcfgpath = $DocRoot + '\etc\mailcfg.xml' # クラス[CommonFunction]のインスタンスを作成、ログ実行(ログ用、logfileにWrite-Outputをするメソッドをもつクラス(省略)) $CF = [CommonFunction]::new() $CF.logfile = $DocRoot + '\log\AlertLoginError_' + $YMD + '.log'; $CF.logwrite("INF", "AlertLoginError process start.") # クラス[GetEventLog]のインスタンスを作成、メソッド実行(イベントログ取得用) $GE = [GetEventLog]::new() $str = $GE.GetEvents() # 返り値(文字列)のLengthが0じゃなかったらメール送信 if ($str.Length -ne 0) { $SM = [SendMail]::new() $SM.ConfigMailProperty($str, $mailcfgpath, $logfile) $CF.logwrite("INF", "Alert mail sended.") } $CF.logwrite("INF", "AlertLoginError process Finished.")
【ソース(class\Class_GetEvents.psm1、イベントログ抽出処理)】
class GetEventLog { # Get-WinEventで得られるオブジェクトを変数に格納 [string] GetEvents() { $MESSAGES = Get-WinEvent ` -logname security ` -FilterXPath ` "Event [ ` System [ ` Provider [@Name='Microsoft-Windows-Security-Auditing'] ` and ( ` EventID='4625' ) ` and ( ` TimeCreated[timediff(@SystemTime) <= 300000] ` ) ` ] ` ]" # オブジェクトの必要な要素のみJsonとして文字列にする(流用しやすいデータにしたいため) $str = $MESSAGES | ForEach-Object { if ( $_.id -eq "4625" ) { $_ | Select-Object ` @{Name = "TimeCreated"; Expression = { $_.TimeCreated.ToString("yyyy-M-d HH:mm:ss") } }, ` Id, ` @{Name = "TargetLogonId"; Expression = { $_.properties[5].value } }, ` @{Name = "TargetDomainName"; Expression = { $_.properties[6].value } }, ` @{Name = "TargetUserSid"; Expression = { $_.properties[4].value } }, ` @{Name = "IpAddress"; Expression = { $_.properties[19].value } }, ` @{Name = "Message"; Expression = { $_.Message.Substring(0, 18) } } } } | ConvertTo-Json return $str } }
【ソース(class\Class_Sendmail.psm1、メール送信処理)】
class SendMail{ [string]ConfigMailProperty($str, $mailcfgpath, $logfile) { $Body = "Windows events LoginError catched. `r`n`r`n EventData : `r`n`r`n " + $str $Result = $this.SendmailbyPS($Body, $mailcfgpath, $logfile) return $Result } [string]SendmailbyPS($Body, $mailcfgpath, $logfile) { $Mailconf_info = [xml](Get-Content $mailcfgpath) foreach ($data in $Mailconf_info.config.data) { } foreach ($data2 in $Mailconf_info.config.data) { } $MailSv = $data.MailSv $Port = $data.Port $Encode = $data.Encode $uid = $data.uid $pwd = $data.pwd $From = $data.from $To = $data.To $Cc = $data.Cc $Subject = $data.Subject #Credential $pwd = $pwd | ConvertTo-SecureString -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential $uid, $pwd #Send Mail try { Send-MailMessage ` -To $To ` -Cc $Cc ` -From $From ` -SmtpServer $MailSv ` -Credential $cred ` -UseSsl ` -Encoding $Encode ` -Port $Port ` -Subject $Subject ` -Body $Body $result = 'OK' } catch { $ErrorMessage = $_.Exception_Message $result = $ErrorMessage + $LASTEXITCODE Write-Output $LASTEXITCODE | Out-File -Append $logfile -encoding Default } if ($result -ne 'OK') { $MailSv = $data2.MailSv $Port = $data2.Port $Encode = $data2.Encode $uid = $data2.uid $pwd = $data2.pwd $From = $data.from $pwd = $pwd | ConvertTo-SecureString -AsPlainText -Force $cred = New-Object System.Management.Automation.PSCredential $uid, $pwd try { Send-MailMessage ` -To $To ` -Cc $Cc ` -From $From ` -SmtpServer $MailSv ` -Credential $cred ` -UseSsl ` -Encoding $Encode ` -Port $Port ` -Subject $Subject ` -Body $Body $result = 'OK' } catch { $ErrorMessage = $_.Exception_Message $result = $ErrorMessage + $LASTEXITCODE Write-Output $LASTEXITCODE | Out-File -Append $logfile -encoding Default } } return $result } }

【説明】
・タスクスケジューラでイベントIDで発火するように、上記のようなトリガ設定でPowershellスクリプトを実行します。
・PowershellスクリプトではイベントログをFilterXPathでフィルターし、ConvertTo-Jsonでイベントログオブジェクトを文字列にしてメールします。
・イベントログの仕様上、一回のログイン失敗に見えても、数回 ID4625のイベントログが記録されることがあります。
【総評】
Classで分けることである程度わかりやすいスクリプトが書けるようになっていると思います。
FilertXPathやForEach-Objectのところで条件を変えれば、いろんな要件も満たせそうですね。
まあでも、業務用途の場合ちゃんとした商用監査ソフトを使うのが一番でしょうね・・・
【Powershell】(Tips集)PowerShellよく使うコードの紹介
今回はPowerShellのスクリプト作成時に、自分がよく使うコードをTipsとして紹介します。
1.実行場所(カレントディレクトリ)をスクリプトのフォルダに指定
Set-Location -Path (Split-Path -Parent $MyInvocation.MyCommand.Path);
2.XML読込
$ConfigXmlPath = "D:\hoge\etc\ConfigHoge.xml"; $XMLConfig = [xml](Get-Content $ConfigXmlPath); foreach ($LogConfig in $XMLConfig.config.LogConfig) { } foreach ($HogeServiceConfig in $XMLConfig.config.HogeService) { } # 参照方法は $LogConfig.LogFol のように。下記なら"yyyyMMdd_Query.log"の文字列が生成される。 $TodayLogFileName = [string]::Join("", (Get-Date).ToString("yyyyMMdd"), $LogConfig.LogFileName);
xmlファイル(ConfigHoge.xml) <?xml version="1.0"?> <config> <LogConfig> <LogFol>D:\</LogFol> <LogFileName>_Query.log</LogFileName> </LogConfig> <HogeService> <PhpExePath>D:\app\php\php.exe</PhpExePath> <HogeProductFol>D:\hoge\laravelsite\</HogeProductFol> </HogeService> </config>
3.連想配列(string, string)定義、追加、抽出
$ArrString = @{ }; $ArrString.Add( "Mysql", $MysqlMessage ); $ArrString.GetEnumerator() | Where-Object { $_.Value -eq "だめぽよ" } | ForEach-Object { $Message = "今夜は" + $_.Key + "が、" + $_.Value + "<br />" }
4.簡単なクラスの扱い方
.("./class/Class_Yomikomare.ps1"); # スクリプト冒頭で読込 $Yomikomare = New-Object YomikomareClass($ValueForConstructor); #インスタンス化、コンストラクタへ変数渡す $ResultValue = $Yomikomare.StartHoge(); # クラスメソッド実行
5.ファイルコピー等
if ((Test-Path $this.FilePath) -eq $false) { # ファイルパスにファイルが存在しないなら New-Item $this.FilePath; # ファイル作成 icacls $this.FilePath /grant Everyone:F; # Everyone権限付与 }
6.Y秒間ごとにX回繰り返し
$maxrepeat = 30 # X回 do { $Status = $this.IsMentananceMode_Larvel() # クラスメソッドなど判定を行える変数を返せるメソッド等 $maxrepeat-- # 繰り返しを進める Start-Sleep -Milliseconds 1000 # Y秒間sleepする } until ($Status -eq $false -or $maxRepeat -eq 0) # 変数の状態と回数をチェックし、どちらかの条件にtrueになるまで続ける
6.Webリクエスト定型(Getのみ最小限)
try { $Response = Invoke-WebRequest -Uri $WebURL -Method Get $StatusCode = $Response.StatusCode } catch { $StatusCode = $error[0].Exception.Response.StatusCode.value__ }
7.タスクスケジューラ起動
$TaskName = "Honyararatask"; if (((Get-ScheduledTask -TaskName $TaskName).State) -ne "Running") { try { Get-ScheduledTask -TaskName $TaskName | Start-ScheduledTask } catch { return $_.Exception } }
とりまこんなところで!