PowerShellでSSMのポートフォワーディングを試してみた(けど駄目だったはなし)

2019.09.26

しばたです。

先月のはなしなのですがAWS Systems Manager(以後SSM) セッションマネージャーにポートフォーワードの機能が追加され、Developers.IOでも以下の記事で紹介されています。

AWS System Managerセッションマネージャーでポートフォワードする

AWS公式の機能紹介はこちら。

このポートフォーワード機能は非常に便利で、以前私が書いた記事で紹介したRDP接続をSSHのポートフォーワードで行うことが踏み台となるSSHサーバー無しに実現可能になります。

クライアント環境にAWS CLISession Manager Plugin for the AWS CLIをインストールしておけば、

# PowerShell コンソール上で実施する場合

$PROFILE_NAME = 'test-profile'
$INSTANCE_ID = '<your ec2 instance id>'
aws ssm start-session --target $INSTANCE_ID `
  --document-name AWS-StartPortForwardingSession `
  --parameters '{\"portNumber\":[\"3389\"],\"localPortNumber\":[\"13389\"]}' `
  --profile $PROFILE_NAME

といったコマンド一発で踏み台無しにRDP接続することができます。

ご覧の通り非常に楽にRDP接続できます。

PowerShellでSSMのポートフォワーディングを試してみた

この機能はAWS CLIの利用が必須となっており、仕組みとしてはaws ssm start-sessionコマンドがsession-manager-plugin(Session Manager Plugin)を起動しプロセス間通信を経てSSMのエンドポイントにコネクションを張るものとなっています。
Windows環境においてですが、Process Explorerでポートフォーワード中のプロセスを見てると下図の様にaws.exesession-manager-plugin.exeを呼び出していることがわかります。

Start-SSMSession

ここでAWS Tools for PowerShellにはaws ssm start-sessionコマンドと対になるStart-SSMSessionコマンドレットが存在します。
ただ、残念ながらこのコマンドレットはsession-manager-pluginと連携することは無く、実行した結果は単純にSSMのセッション情報を返して終わります。

# Start-SSMSession はただセッション情報を返すだけ
Import-Module AWSPowerShell.NetCore
Set-DefaultAWSRegion -Region ap-northeast-1
Set-AWSCredential -ProfileName your-profile

$INSTANCE_ID = '<your ec2 instance id>'
$params = @{
    Target = $INSTANCE_ID
    DocumentName = 'AWS-StartPortForwardingSession';
    Parameter = @{
        portNumber = '3389'
        localPortNumber = '13389'
    };
}
$session = Start-SSMSession @params
$session | Format-List

(単純にセッション情報を返してコマンドレットは終了する)

session-manager-plugin の引数

Start-SSMSession単体ではsession-manager-pluginを起動してくれないことがわかりましたので、どうにかsession-manager-pluginを起動できないかとsession-manager-pluginが取りうる引数を調べてみることにしました。

session-manager-pluginはソースが公開されておらず、適当に引数を渡してみても役に立つ情報は得られませんでした。

そこで呼び出し元のAWS CLIのソースを読んでみたところ、

# sessionmanager.py のソースを一部抜粋

# call executable with necessary input
check_call(["session-manager-plugin",
            json.dumps(response),
            region_name,
            "StartSession",
            profile_name,
            json.dumps(parameters),
            endpoint_url])

と、

  • 第1引数 : SSMセッション情報(JSON)の文字列
  • 第2引数 : リージョン名
  • 第3引数 : "StartSession"固定
  • 第4引数 : プロファイル名 (プロファイル未指定の場合は '')
  • 第5引数 : aws ssm start-sessionコマンドに渡したパラメーターをJSONにしたもの
  • 第6引数 : SSMエンドポイントのURL

であることがわかりました。

ここで厄介なのが第4引数のプロファイル名で、こちらはAWS CLIで設定したプロファイル名をそのまま引き渡しており、session-manager-pluginはAWS CLIと認証情報を共有しています。

仕組み上AWS Tools for PowerShellとAWS CLIは認証情報は別となっており相互に変換する機能はありません。どうにかAWS Tools for PowerShellで完結させたいと思うものの、AWS CLIの認証情報が必要となってしまうと素直にAWS CLIを使用するしかなく、また、そうするのが現実的です。

最後に

ざっとこんな感じでした。
タイトルに記載している様にAWS Tools for PowerShellを使いSSMのポートフォーワード機能を利用するのは現実的ではなく大人しくAWS CLIを利用したほうが良いことがわかりました。

今後AWS Tools for PowerShellまたはsession-manager-plugin側で何らかの機能更新がありPowerShellからSSMのポートフォーワード機能が使える様になるとPowerShellおじさんの私としては嬉しいのですが、おそらく、現実は厳しいままであると思います。