Closed7

Windowsでしか動かないコマンドラインツールをAWS上で1日1回動かしたい

masaminhmasaminh

やりたいこと

Windowsでしか動かないCLIアプリ1日1回動かしたい。

動かしたいWindowsアプリの状況

  • 原則CLIのみで動作だが、初期設定時などのタイミングでGUI操作が必要になる。
    • 今時ActiveXで提供されているライブラリを使わないといけない都合。GUIはライブラリ側で表示してる。
  • 1回あたりの動作時間は数分程度。
  • アプリの出力結果はローカルファイルやS3に格納できるが、最終的にはS3に格納したい。

作戦

  • Windows EC2で動作させる。
  • 初期設定時などのGUI操作はRDP接続で実施。
  • 1日1回の実行はEventBridgeを用いてスケジュール化。
  • Windows内でのタスクスケジューラを使って、該当アプリを含むバッチファイルを起動する。
    • 繰り返し間隔を短くして、多重実行しない設定にすれば、EC2起動後すぐに走らせられるのではないかと推測。
  • タスクスケジューラから起動するバッチファイルでは、処理の最後にシャットダウンを走らせる。
  • 念の為、EventBridgeで、起動時刻+1時間後くらいにEC2をシャットダウンするようスケジュールしておく。
masaminhmasaminh

本質じゃないけどちょっとメモ。

CDKにて以下のイメージでEC2を作成

    const machineImage = new ec2.WindowsImage(
      ec2.WindowsVersion.WINDOWS_SERVER_2022_JAPANESE_FULL_BASE,
    );

キーボードが英語レイアウト前提になっていたので、
Windowsの設定→時刻と言語→言語→優先する言語→日本語→オプション→ハードウェアキーボードレイアウト

「日本語キーボード (106/109 キー)」
に変更後再起動。

でも自分のMacからのRDP接続では相変わらず英語レイアウトになっている。
https://blog.g-gen.co.jp/entry/localize-windows-server-on-gce#参考情報
を参考にしてレジストリ変更することで日本語キーボードとして入力できた。

masaminhmasaminh

これまた本質ではないけど。。

↑のイメージで作ったWindows EC2ってAWS CLIは入ってないんですね。
手動でAWS CLIをインストールして対応。

masaminhmasaminh

これまた本質ではないけど。。

キーペアは以下のようにすれば、作成した上でEC2インスタンスに紐付け可能。
※ここではキー名はスタック名にしてしまっているけど、そこはよきに。

    const keyPair = new ec2.CfnKeyPair(this, 'KeyPair', {
      keyName: this.stackName,
    });

    new ec2.Instance(this, 'Instance', {
      //もろもろ省略
      keyName: keyPair.ref,
    });

キー自体に関しては、パラメータストア「/ec2/keypair/キーID」に格納されてる。

masaminhmasaminh

Windows内でのタスクスケジューラを使って、該当アプリを含むバッチファイルを起動する。
繰り返し間隔を短くして、多重実行しない設定にすれば、EC2起動後すぐに走らせられるのではないかと推測。

以下の設定でいけそう。

  • 全般
    • ユーザーがログオンしているかどうかにかかわらず実行する
  • トリガー
    • 設定
      • 1回
    • 詳細設定
      • 繰り返し間隔:5分
      • 継続期限:無期限
  • 設定
    • タスクが既に実行中の場合に適用される規則:新しいインスタンスを開始しない
masaminhmasaminh

タスクスケジューラから起動するバッチファイルでは、処理の最後にシャットダウンを走らせる。

シャットダウンをバッチの最後に入れるのはやめた。
メンテナンスが困難になりそうなので。

masaminhmasaminh

1日1回の実行はEventBridgeを用いてスケジュール化。
念の為、EventBridgeで、起動時刻+1時間後くらいにEC2をシャットダウンするようスケジュールしておく。

CDKにてEC2の起動・停止を実装。
普通に実装するなら、EventBridge Schedulerを使うのが筋なんでしょうけど、まだL2 Constructがない…
じゃぁ、昔ながらのEventBridgeルールでやるか…となったのですが、EventBridgeルールのターゲットとしてSSMオートメーションを使おうとすると、やっぱりL2 Constructがない…
LambdaでEC2の起動・停止を書いてもいいけど、そのためにLambda使うのもなんだな…ということで、結局、EventBridgeルール→SSMオートメーションでのEC2起動・停止をL1 Construct使って実装することにしました。

    const startStopRole = new iam.Role(this, 'StartStopRole', {
      assumedBy: new iam.ServicePrincipal('events.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AmazonSSMAutomationRole'),
      ],
    });

    new events.CfnRule(this, 'StartRule', {
      scheduleExpression: 'cron(30 12 * * ? *)',
      targets: [
        {
          arn: `arn:aws:ssm:${region}::automation-definition/AWS-StartEC2Instance:$DEFAULT`,
          id: 'StartEC2Instance',
          input: `{"InstanceId": ["${instance.instanceId}"]}`,
          roleArn: startStopRole.roleArn,
        },
      ],
    });

    new events.CfnRule(this, 'StopRule', {
      scheduleExpression: 'cron(45 12 * * ? *)',
      targets: [
        {
          arn: `arn:aws:ssm:${region}::automation-definition/AWS-StopEC2Instance:$DEFAULT`,
          id: 'StopEC2Instance',
          input: `{"InstanceId": ["${instance.instanceId}"]}`,
          roleArn: startStopRole.roleArn,
        },
      ],
    });
このスクラップは2024/01/18にクローズされました