invoke-commandのScriptBlockをMySQLから読み込み実行させてみたときのメモ
パーソナルなIT雑務を想定して、Windowsのリモートホストに定型的なコマンドをmysqlから読み込み発行するシナリオでスクリプトを書いてみた。
Table Schema
oid(operation id)はscriptをグループ化させる想定で追加してある(not null制約がないのは失敗)。
mysql> desc operation; +--------+---------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+---------------+------+-----+---------+-------+ | id | int | NO | PRI | NULL | | | host | varchar(128) | YES | | NULL | | | uid | varchar(128) | NO | | NULL | | | oid | int | YES | | NULL | | | script | varchar(1024) | YES | | NULL | | | secure | varchar(1024) | YES | | NULL | | +--------+---------------+------+-----+---------+-------+ 6 rows in set (0.02 sec)
データ検索はpowershellでは少々、厳しそうだったので素直にpython scriptに任せた。
python scriptは結果をdictの配列で返し、powershell側でjson形式の文字列をconvertfrom-jsonでPSCustomObjectに変換する。
Python Script
import MySQLdb import sys import os av = sys.argv if len(av) < 2: print('oid is Mandatory') exit() else: oid = av[1] mysql_user = os.environ['mysql_user'] mysql_passwd = os.environ['mysql_passwd'] computername = 'localhost' dbname = 'db1' conn = MySQLdb.connect(user=mysql_user,passwd=mysql_passwd,host=computername,db=dbname) cur = conn.cursor() placeholder = "select * from operation where oid = {} order by id" sql = placeholder.format(oid) cur.execute(sql) rows = cur.fetchall() conn.close() keys = ['id','host','uid','oid','script','secure'] arr = [] for row in rows: d = {} for k,v in zip(keys,row): d[k] = v arr.append(d) print(arr)
リモートホストには invoke-command で発行するが、scriptblock はべた打ちのときは { } の中にコマンドの文字列を書き込めばよい。しかし、コマンド文字列を変数に格納している場合は、{ }の中にコマンドの文字列を表示させても失敗する。
型変換はできないので、[Scriptblock]::Create()関数で文字列からscriptblockを生成しなければならない。
OperationIDは'ValueFromPipeline=$True'としてパイプラインから受け取れるようにしている、必要性はないのだが・・・。なお、不要でもbegin{},end{}を入れているのはadvanced functionを書く時の癖としている。
PowerShell Script
# # Invoke Operation # e.g. invoke-operation -operationid 10 # Function Invoke-Operation { [CmdletBinding()] param( [Parameter(Mandatory=$True, ValueFromPipeline=$True)] [ValidateNotNullOrEmpty()] [Int]$OperationID ) begin { } process { $cmd = "python '${home}\Documents\Python Scripts\get_data_mysql.py' $OperationID" $ret = ( invoke-expression $cmd ) $ops = ( convertfrom-json $ret ) foreach ( $h in $ops ){ $pcname = ${h}.host $uid = ${h}.uid $cmdstr = ${h}.script $cmdstr $script = [Scriptblock]::Create($cmdstr) $secure = ${h}.secure $pw = ConvertTo-SecureString -String $secure $c = New-Object System.Management.Automation.PSCredential($uid,$pw) invoke-command -computername $pcname -credential $c -scriptblock $script } } end { } }
ConvertFrom-SecureStringでセキュリティで保護された文字列 (System.Security.SecureString) を暗号化された標準文字列 (System.String) に変換して格納してあるので、ConvertTo-SecureStringで元に戻す。
ネットで調べると以下の関数で平文への復号が可能なようなので単なる気休めに過ぎないのかもしれない。しかし、結構、気休めになるのでは。
- [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR()
- [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR()
AsJobオプションを使用する場合は以下が参考になるかも。私には読み込む気力はなかったが・・・
www.vwnet.jp
script data
mysql> select oid,script from operation; +------+--------------------------------------------------------------------------- | oid | script +------+--------------------------------------------------------------------------- | 11 | docker container start python1 | 10 | $c = docker container ls -aq -f name=test-.+; docker container start $c | 20 | docker container ls --format="table{{.ID}} {{.Names}} {{.Ports}}" | 30 | 1..3 | % { $n = 'test-' + $_;$n;$c = docker container ls -q -f name=$n;... | 50 | $c = docker container ls -q; docker container stop $c | 90 | stop-computer -force | 99 | +------+--------------------------------------------------------------------------
execution example
PS >invoke-operation -operationid 10 $c = docker container ls -aq -f name=test-.+; docker container start $c d57707775ada 4b4ca2b65be2 b76c836b59fc PS >invoke-operation -operationid 20 docker container ls --format="table{{.ID}} {{.Names}} {{.Ports}}" CONTAINER ID NAMES PORTS d57707775ada test-3 0.0.0.0:8093->80/tcp 4b4ca2b65be2 test-2 0.0.0.0:8092->80/tcp b76c836b59fc test-1 0.0.0.0:8091->80/tcp PS >invoke-operation -operationid 30 1..3 | % { $n = 'test-' + $_;$n;$c = docker container ls -q -f name=$n; docker container exec $c hostname -i } test-1 172.17.0.4 test-2 172.17.0.3 test-3 172.17.0.2 PS >invoke-operation -operationid 50 $c = docker container ls -q; docker container stop $c d57707775ada 4b4ca2b65be2 b76c836b59fc PS >invoke-operation -operationid 90 stop-computer -force
了