Amazon WorkSpacesを毎朝始業直前に自動起動する仕組みを作ってみた

Amazon WorkSpacesを実行モード「AutoStop」で利用する場合、起動時に待たされるというのが少しネックですよね。 そこで、ちょっとした仕組みで「自動起動」を実現してみました。
2020.04.15

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

Amazon WorkSpaces には、常時起動する「AlwaysOn」と、一定時間利用されないと自動的に停止する「AutoStop」の2通りの実行モードがあります。

「AutoStop」を採用するとランニングコストを抑えることができるのですが、WorkSpaceが停止状態にある時にWorkSpacesクライアントやWebブラウザから接続して利用しようとすると、起動するのに数分待つ必要があります。

そこで、毎朝始業時刻の数分前になったら自動的にWorkSpaceを起動しておいて、クライアントから接続するとすぐに利用できるような仕組みを作ってみました。

自動起動を実現する仕組み

WorkSpaces環境に存在する仮想デスクトップ (WorkSpace) を起動するLambda関数を作成します。

そして、EventBridgeのスケジュールを使ってLambda関数を毎朝決められた時刻に実行します。

Lambda関数

lambda_function.py

import boto3
import os

client = boto3.client('workspaces')

def lambda_handler(event, context):
    target_directory_id = os.environ.get('TARGET_DIRECTORY_ID', '')
    target_tag_key      = os.environ.get('TARGET_TAG_KEY', '')
    target_tag_value    = os.environ.get('TARGET_TAG_VALUE', '')
    check_workspaces(target_directory_id, target_tag_key, target_tag_value)

def check_workspaces(target_directory_id, target_tag_key, target_tag_value):
    # WorkSpaceのリストを取得
    if target_directory_id == '':
        response = client.describe_workspaces()
    else:
        response = client.describe_workspaces(DirectoryId=target_directory_id)

    # 全てのWorkSpaceについて繰り返し
    for workspace in response['Workspaces']:
        # WorkSpaceのID
        workspace_id = workspace['WorkspaceId']

        # 対象判定: タグに特定キー/値が設定されている場合のみを対象とする
        flg = False
        response = client.describe_tags(ResourceId=workspace_id)
        for tag in response.get('TagList'):
            if (tag.get('Key') == target_tag_key) and (tag.get('Value') == target_tag_value):
                flg = True
                break
        if flg == False:
            print('WorkSpace "{0}" skipped: Specified tag is not attached.'.format(workspace_id))
            continue

        # 対象判定: ステータスが「STOPPED」の場合のみを対象とする
        if workspace.get('State') != 'STOPPED':
            print('WorkSpace "{0}" skipped: State is not STOPPED.'.format(workspace_id))
            continue

        # WorkSpaceが対象であった場合、起動を試みる
        r = start_workspace(workspace_id)
        if r == 0:
            print('WorkSpace "{0}" started successfully.'.format(workspace_id))
        else:
            print('WorkSpace "{0}" tried to start, but an error occurred while starting.'.format(workspace_id))

def start_workspace(workspace_id):
    # IDで指定されたWorkSpaceを起動する
    response = client.start_workspaces(
        StartWorkspaceRequests=[
            {
                'WorkspaceId': workspace_id
            },
        ]
    )

    # エラー判定
    failed_requests = response.get('FailedRequests')
    if not failed_requests:
        return 0
    else:
        print('[Error] {0}'.format(failed_requests))
        return 1

Lambda関数の処理内容ですが、以前公開したブログ記事を応用したものになっています。

AWS SDK for Python (Boto3) の “Client API” と “Resource API” の違いについて調べてみた | Developers.IO

今回のLambda関数の大まかな処理の流れは、以下の通りです。

  • describe_workspaces() メソッドを使って全てのWorkSpaceの情報を取得する
  • for ~ in ~ 構文により、WorkSpaceの情報を一つずつ取り出して処理する
  • タグの付与状況およびWorkSpaceのステータスを確認して、対象か否かを判定する
  • 対象であれば、start_workspaces() メソッドを使ってWorkSpaceを起動する

設定手順

1. Lambda関数を作成する

Lambda関数を「一から作成」します。

  • 関数名: warmup-workspaces
  • ランタイム: Python 3.8
  • IAMロール: 基本的なLambdaアクセス権限で新しいロールを作成

Lambda関数が作成されたら、前述のコード内容を貼り付けます。

環境変数を設定します。(「編集」をクリック)

以下のように環境変数を追加します。

  • TARGET_DIRECTORY_ID: 自動起動の対象とするWorkSpacesディレクトリを指定 (省略すると全てのディレクトリが対象となります)
  • TARGET_TAG_KEYTARGET_TAG_VALUE: ここで指定したタグが付与されたWorkSpaceのみが自動起動の対象となります

対象のWorkSpaceの数にもよりますが、数が多い場合にはLambda関数のタイムアウトを長めに変更しておいた方が良いでしょう。

2. IAMロールのアクセス権限ポリシーを設定する

Lambda関数の実行IAMロールに対して、WorkSpacesへの必要なアクセス権限ポリシーを追加します。

まず、IAMポリシーを以下の内容で作成します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "workspaces:DescribeWorkspaces",
        "workspaces:StartWorkspaces",
        "workspaces:DescribeTags"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

Lambda関数の実行IAMロールを開いて、さきほど作成したIAMポリシーをアタッチします。

3. EventBridgeのスケジュールを設定する

Lambdaの画面に戻り、「トリガーを追加」をクリックします。

トリガーを以下のように設定します。

  • トリガーの種類: CloudWatch Events/EventBridge
  • ルール: 新規ルールの作成
  • ルール名: warmup-workspaces-schedule
  • ルールタイプ: スケジュール式
  • スケジュール式: cron(55 23 ? * SUN-THU *)
  • トリガーの有効化: チェックを入れる

スケジュール式は、cronの記述方法に準拠します。(UTCでの記述になることに注意してください)

この例では、UTCで「毎週 日曜日から木曜日、23:55に実行」を指定していることになります。
(日本時間に変換すると「毎週 月曜日から金曜日、08:55に実行」となります)

※ スケジュール式の記述方法の詳細については、AWSドキュメントを参照してください。
ルールのスケジュール式 - Amazon EventBridge

以下のように、Lambda関数のトリガーとして「CloudWatch Events/EventBridge」が設定されたことを確認します。

4. 対象としたいWorkSpaceにタグを付与する

Lambda関数の設定手順で説明した通り、特定のタグが付与されたWorkSpaceのみが自動起動の対象となります。

対象としたいWorkSpaceを選択した状態で、「アクション」→「タグの管理」を選択します。

Lambda関数の環境変数で指定した通りのタグを付与します。

これで、全ての準備は完了です。

動作確認

スケジュールで指定した時刻まで待って、WorkSpaceが自動的に起動することを確認しましょう。

各WorkSpaceの状態は以下のようになっています。

  • 4番目のWorkSpaceのみ起動状態、その他は停止状態
  • 5番目のWorkSpace以外は指定したタグを付与済み

スケジュールで指定した時刻になると、以下のようになりました。

1~3番目のWorkSpaceが自動的に起動しました!

4番目のWorkSpaceは、ステータスが「停止 (STOPPED)」ではなかったため、処理が行われませんでした。
5番目のWorkSpaceは、指定したタグが付与されていないため、対象外となりました。

Lambdaの実行ログは以下のようになっています。

START RequestId: 6b545e30-f57a-4025-8c7b-0b155f6ce203 Version: $LATEST
WorkSpace "ws-gk22b70mp" started successfully.
WorkSpace "ws-f7b1gt1mt" started successfully.
WorkSpace "ws-jnl1ww1y6" started successfully.
WorkSpace "ws-h1gj8cb07" skipped: State is not STOPPED.
WorkSpace "ws-79v5p552g" skipped: Specified tag is not attached.
END RequestId: 6b545e30-f57a-4025-8c7b-0b155f6ce203
REPORT RequestId: 6b545e30-f57a-4025-8c7b-0b155f6ce203	Duration: 2672.77 ms	Billed Duration: 2700 ms	Memory Size: 128 MB	Max Memory Used: 71 MB	Init Duration: 304.35 ms	

「自動起動」が意味の無い場合がある??

せっかく作ってみた「自動起動」の仕組みですが、シチュエーションによっては「使う意味が無い」場合があります。

このことを説明するためには、実行モード「AutoStop」と「AlwaysOn」の料金体系を確認する必要があります。

料金 - Amazon WorkSpaces | AWS

料金表の「月額料金」が「AlwaysOn」、「時間料金」が「AutoStop」の料金です。

WoakSpacesのバンドル (スペック) の選択にもよりますが、概ね、月の利用時間が「80時間」を超えたところで「AlwaysOn」と「AutoStop」の料金が逆転します。

例えば「月曜日から金曜日まで、1日8時間」利用したとすると、月の利用時間は約160~180時間ほどになります。

この場合、価格的に「AutoStop」を使うメリットは無いため、必然的に「AlwaysOn」を採用することになります。

つまり、常時起動しているため、今回の自動起動の仕組みを使う必要が無いということになってしまいます。

おわりに

実行モード「AutoStop」を採用している場合の使い勝手の改善を期待して、WorkSpacesを自動起動する仕組みを実装してみました。

上で述べたように、WorkSpacesの利用状況によってはあまり使えないかもしれませんが、例えば「週に1、2日だけ利用する」「1日に2~3時間だけ利用する」といった場合であれば有効活用できるかもしれません。

結果的に「技術的な実験デモ」のようになってしまった感もありますが、今回ご紹介した「仕組み」が何かの参考になれば幸いです。