PowerShell の連想配列ですべてを逆から処理する方法
PowerShell ムズカシイデス。那須です。
PowerShell で連想配列に入ってるものを順に処理したり順番関係なく処理したりするのは簡単なんですが、逆から処理するのっていいやり方がなくて困ってたりしませんか? そもそも逆順で処理するシーンがあまりないのかもしれませんね。
スクリプトの作りが悪い、という話もありますが、極力シンプルにかつ使い回せるインフラのスクリプトを作りたくてこんな悩みを持っていました。 例えばサービスの起動停止スクリプトで、設定ファイルに起動する順番通りにサービス名を書いてもらうけど、逆順で同じようなことを書きてほしくないですよね? そもそも二度手間ですし。 今回は無理やりなんとかしたので、それを忘れないために書き残しておきます。
そもそも連想配列の順番って固定できるの?
とりあえず何も考えずに連想配列を作って表示してみましょう。
PS C:\> $hashtable = @{AAA = "aaa", "bbb"; BBB = "ccc","ddd" } PS C:\> $hashtable Name Value ---- ----- BBB {ccc, ddd} AAA {aaa, bbb}
とまあ、キーが逆になってますね。 値の順番はそのままですが。
で、キー AAA から処理したい場合は、連想配列の初期化時に [ordered] をつけます。
PS C:\> $hashtable = [ordered]@{AAA = "aaa", "bbb"; BBB = "ccc","ddd" } PS C:\> $hashtable Name Value ---- ----- AAA {aaa, bbb} BBB {ccc, ddd}
これでとりあえず順番は指定できそうです。
逆順にする何かがあるのでは?
と思いましたので、リストならどうするのか調べてみると Reverse で簡単にできることがわかりました。
PS C:\> $List = @("aaa", "bbb", "ccc", "ddd") PS C:\> $List aaa bbb ccc ddd PS C:\> [array]::Reverse($List) PS C:\> $List ddd ccc bbb aaa
案外連想配列でもできるんじゃね?って思ってやってみました。
PS C:\> [array]::Reverse($hashtable) PS C:\> $hashtable Name Value ---- ----- AAA {aaa, bbb} BBB {ccc, ddd}
何も起こりませんでした… 続きまして hashtable 指定でやってみると、
PS C:\> [hashtable]::Reverse($hashtable) [System.Collections.Hashtable] に 'Reverse' という名前のメソッドが含まれないため、メソッドの呼び出しに失敗しました。 発生場所 行:1 文字:1 + [hashtable]::Reverse($hashtable) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) []、RuntimeException + FullyQualifiedErrorId : MethodNotFound
そんなことできません!とのことです。 他にもいろいろ検索してみたんですが、これだ!って解決策がなかったので、普通に頭で考えてたソートをコード化しました。 $hashtable は [ordered] 指定してください。
function hashtable逆から処理する { param([System.Collections.Specialized.OrderedDictionary]$hashtable) $keyList = @() foreach ($key in $hashtable.Keys) { $keyList += $key } [array]::Reverse($keyList) foreach ($key in $keyList) { $contentList = @() foreach ($content in $hashtable[$key]) { $contentList += $content } [array]::Reverse($contentList) $hashtable[$key] = $contentList } foreach ($key in $keyList) { foreach ($content in $hashtable[$key]) { 処理 } } }
要は、
- キーを逆順ソートして別のリストに入れる
- 値も取り出して逆順ソートしてリストに入れたもので定義し直す
- 逆順ソートしたキーのリストをキーとして foreach する
だけです。 私の小さい脳みそではこうするしかありませんでした。
実は他にもっと簡単なやり方あるのでは?
と今でも思ってたりします。 ので、もしご存知の方がいたら鼻で笑いながらでもいいのでツッコミいただければ嬉しいです!
CloudTrail のログに何かされたら気づける仕組みを考えた
令和元年もよろしくお願いします。那須です。
久々に AWS の普段気にしないことの勉強をしています。 普段気にしない AWS サービスといえば、やっぱり CloudTrail ですよね? CloudTrail のドキュメントを改めて読み返していたら、↓こんなドキュメントを見つけました。
ログを S3 や CloudWatch Logs に書き込めるのはご存知だと思いますが、その出力されたログがそもそも改ざんされない前提で運用されてませんか? 私はそうしてました。 今回は、CloudTrail のログが改ざんされたり削除されたりした場合に気づける仕組みを考えてみましょう。
CloudTrail ログファイルの整合性チェックスクリプト
このためだけに EC2 インスタンスを起動するのもちょっと違うなーと思うので、Lambda で動くようにしてみました。
してみましたと言っても、いろんな方が公開されているものをパクって利用させていただいております。
先ほどの AWS ドキュメントで、
CloudTrail ログファイルの整合性を検証するには、AWS CLI を使用するか、独自のソリューションを作成することができます。
とあります。 Boto3(Python3.7) で作ろうかと思ったんですが、一から整合性検証の流れを書かないといけないことがわかったので AWS CLI で書くことにしました。
まず、Lambda で bash が動くように↓この Lambda Layer を使っています。 必要な設定等はこの README を確認してみてください。
めでたく bash を実行できるようになったら、↓このようなスクリプトを書きます。 これを lambda_function.sh という名前に保存します(拡張子を .sh にしましょう
lambda_handler () { set -e accountid=`aws sts get-caller-identity | jq -r .Account` starttime=`date "+%Y-%m-%dT00:00:00Z" -d "${days_ago} days ago"` aws cloudtrail validate-logs \ --trail-arn arn:aws:cloudtrail:${region}:${accountid}:trail/${audit_name} \ --start-time ${starttime} }
Lambda 環境変数は、↓の 3 つを設定します。
- audit_name : <検証対象の CloudTrail 証跡名>
- days_ago : 1( 1 を指定すると 1 日前の 0:00 から現在までのログを対象に検証します)
- region : <リージョン名>(ap-northeast-1 など)
これで毎日 1 回ずつ実行すると、前日分のログを対象に整合性検証を実行して CloudWatch Logs に結果を出力してくれます。 毎日 1 日前のログファイルを検証するってことはしないと思いますので、確認したい期間と確認したい周期をそのシステムの運用内容に応じて調整してみてください。 ちなみに、実行には約 5 分くらいかかりますので、実行頻度によってはそれなりに課金されるので注意してくださいね。 タイムアウトの設定も伸ばしておきましょう。
異常検知のテスト
改ざん等がない場合
正常時は、↓のようにすべてのファイルが valid と出ます。
Validating log files for trail arn:aws:cloudtrail:ap-northeast-1:384912608626:trail/all_audit between 2019-04-30T00:00:00Z and 2019-05-01T08:20:47Z Results requested for 2019-04-30T00:00:00Z to 2019-05-01T08:20:47Z Results found for 2019-04-30T00:38:36Z to 2019-05-01T07:38:36Z: 32/32 digest files valid 442/442 log files valid
異常検知時
改ざんや削除を検知した場合は、↓のように INVALID と出ます。
Validating log files for trail arn:aws:cloudtrail:ap-northeast-1:384912608626:trail/all_audit between 2019-04-24T00:00:00Z and 2019-05-01T08:06:20Z Results requested for 2019-04-24T00:00:00Z to 2019-05-01T08:06:20Z Results found for 2019-04-24T00:38:36Z to 2019-05-01T06:38:36Z: 175/175 digest files valid 1939/1940 log files valid, 1/1940 log files INVALID Log file s3://nasu-cloudtrail/AWSLogs/384912608626/CloudTrail/ap-northeast-1/2019/04/25/384912608626_CloudTrail_ap-northeast-1_20190425T0005Z_BwtuYZ1JKLOFKhKE.json.gz INVALID: invalid format
この INVALID 出力を基に、通知する仕組みを設定してみましょう。
異常検知時のアクション
こんな感じで、CloudWatch Logs のメトリクスフィルタを設定しましょう。
メトリクスフィルタを設定したら、続けてアラームを設定します。 SNS トピックを指定してメール通知したり、間に他の AWS サービスを挟んで Slack 等に通知することができますよ。 今回はとりあえず動きを確認したいだけなので、SNS でメール通知してみました。
実際にログファイルの中身を変えてから作成した Lambda 関数を実行すると、↓のようなメールが飛んできました。
これでCloudTrail のログになにかあっても気づくことができますね。
まとめ
今まではログファイルは改ざんされてもバージョニング設定してればバージョンが上がるから気づくやろって思ってましたが、そもそもログファイルのバージョンなんていちいち気にしてません。 なので今回のようなテストをしてみました。 本来であれば、必要なユーザ以外は当該バケットへの書き込み等を禁止していればほとんどの場合はこのような場面は防げると思いますが、このご時世何があるかわかりませんよね。 そういう場合に今回の仕組みは役に立つかもしれません。
ここまで書いてみたものの、この仕組みが必要とされる時ってホンマにあるんかな??と思いましたが、まあ 1 人くらいは困ってる方がいると思うことにします…