Help us understand the problem. What is going on with this article?

【ハンズオン】AWS IoT1-Click で AWS Client VPN を起動・停止してみた

やったこと

AWS Client VPN を AWS IoT1-Click のクリック・ダブルクリックで起動・停止(=エンドポイントのサブネットへの関連付け・関連付け解除)するようにしました。

image.png
image.png

AWS Client VPN の料金は、下の引用の通り、Client VPN(エンドポイント)にサブネットを関連付けることで発生します。
関連付けを行ったままだと、夜間など利用しない時間帯にも料金が発生してしまうため、利用する時だけサブネットの関連付けを行うようにしました。

AWS Client VPN では、アクティブなクライアント接続の数に対して、
および Client VPN に関連付けられているサブネットの数に対してそれぞれ 1 時間単位の料金が発生します。

引用元:AWS Client VPN の料金

前提

  • IoTデバイスとして、AWS IoT エンタープライズボタンが必要です。今回はLTE-MのeSIM内蔵の SORACOM LTE-M Button powered by AWSを使用しました。

  • サブネットの関連付けを解除すると VPN 接続ができなくなり、料金も発生しないため、「サブネットの関連付け = AWS Client VPN の起動」、「サブネットの関連付け解除 = AWS Client VPN の停止」と定義しています。

  • AWS Client VPN の設定手順は、こちらの記事(AWS Client VPN 設定メモ)を参考にして行ったため割愛します。
    (AWS CLI による設定手順がまとめられています。非常に参考になりました、ありがとうございます)

環境

  • IoT デバイス

    • SORACOM LTE-M Button powered by AWS
  • クライアントPC

    • Windows 10 バージョン 1803 (OSビルド 17134.829)
    • PowerShell バージョン 5.1.17134.765
    • AWS CLI バージョン 1.16.148
    • Python バージョン 2.7.15
    • pip バージョン 9.0.1
    • OpenVPN バージョン 2.4.7
  • AWS サービス

    • AWS Client VPN
    • AWS IAM
    • AWS Lambda
    • AWS CloudWatch Logs
    • AWS IoT1-Click

設定手順

手順に出てくる変数には、以下の例のように、予め値を設定してください。

設定例
# 1. IAM の Lambda 実行ロール作成用
$Region="ap-northeast-1"
$RoleName="client_vpn_admin_role"

# 2. Client VPN を操作する Lambda 関数作成用
$FunctionName="manage_client_vpn"
$handlerName="manage_client_vpn_function.lambda_handler"

1. IAM の Lambda 実行ロールを作成する

Lambda 関数は、IAM で作成されたロールを必要とし、ロールは関数を実行するのに必要なアクセス権限を提供します。
ここでは、ロールを作成し、Lambda 関数が「Cloudwatch Logsへのログ出力」と「Client VPN エンドポイントに対するサブネット関連付け/関連付け解除」を実行するのに必要なアクセス権限(ポリシー)をロールへアタッチします。

(予め、AWS CLI を実行するカレントディレクトリへ下記の role-policy.json を作成してください。)

# ロールの作成
aws iam create-role --role-name $RoleName --assume-role-policy-document file://role-policy.json

# ロールの確認
aws iam get-role --role-name $RoleName

# ポリシーのアタッチ
aws iam attach-role-policy --role-name $RoleName --policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess"

aws iam attach-role-policy --role-name $RoleName --policy-arn "arn:aws:iam::aws:policy/AmazonEC2FullAccess"

# アタッチしたポリシーの確認
aws iam list-attached-role-policies --role-name $RoleName
role-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
       {
         "Action": "sts:AssumeRole",
         "Principal": {
           "Service": "lambda.amazonaws.com"
          },
          "Effect": "Allow",
          "Sid": ""
       }
    ]
}

2. Lambda 関数の作成

2.1 デプロイパッケージの作成

任意のディレクトリで、Lamda へデプロイするパッケージを作成します。
(参考:Lambda上でAWSCLIを動かしてS3 Syncするaws-cliでLambdaのScheduled Eventを作成する

mkdir lambda-cli

cd lambda-cli

pip install awscli -t .

作成した lambda-cli ディレクトリに以下の Python スクリプト( aws、manage_client_vpn_function.py )を配置します。

aws
import sys
import awscli.clidriver


def main():
    return awscli.clidriver.main()


if __name__ == '__main__':
    result = main()
    sys.exit(result)
manage_client_vpn_function.py
import subprocess


def lambda_handler(event, context):
    client_vpn_endpoint_id = "[あなたの Client VPN エンドポイント ID]"
    subnet_id_list = ["あなたの 関連付けるサブネット ID(カンマ区切りで複数記述可)"]
    click_type = event['deviceEvent']['buttonClicked']['clickType']
    if (click_type == "SINGLE"):
        message = "The button is clicked once."
        start_client_vpn(client_vpn_endpoint_id, subnet_id_list)
    elif (click_type == "DOUBLE"):
        message = "The button is double clicked."
        stop_client_vpn(client_vpn_endpoint_id)
    elif (click_type == "LONG"):
        message = "The button is long pressed."
    else:
        message = "click_type is unknown."
    print(message)


def start_client_vpn(client_vpn_endpoint_id, subnet_id_list):
    for subnet_id in subnet_id_list :
        cmd = "python aws ec2 associate-client-vpn-target-network --client-vpn-endpoint-id " + client_vpn_endpoint_id + " --subnet-id " + subnet_id
        result = subprocess.run(
        cmd.split(" "),
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT
        )
        print(result.stdout.decode())


def stop_client_vpn(client_vpn_endpoint_id):
    cmd = "python aws ec2 describe-client-vpn-target-networks --client-vpn-endpoint-id " + client_vpn_endpoint_id + " --query ClientVpnTargetNetworks[].AssociationId --output text"
    result = subprocess.run(
        cmd.split(" "),
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT
    )
    association_id_list = result.stdout.decode().strip().split()
    print("[DEBUG] association_id_list: %s" %  association_id_list)

    for association_id in association_id_list:
        cmd = "python aws ec2 disassociate-client-vpn-target-network --client-vpn-endpoint-id " + client_vpn_endpoint_id + " --association-id " + association_id

        result = subprocess.run(
        cmd.split(" "),
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT
        )
        print(result.stdout.decode())

Python スクリプトを作成したら、lambda-cli ディレクトリをZIP圧縮します。

2.2 Lambda へのデプロイ

ZIPファイルを指定して、Lambda 関数を作成します。(bash で実行する場合は、改行文字を「`」→「\」に置換してください)

# ロール ARN の取得
$RoleArn=$(aws iam get-role --role-name $RoleName --query 'Role.Arn' --output text)

# Lambda 関数の作成
aws lambda create-function `
    --function-name $FunctionName `
    --runtime python3.7 `
    --role $RoleArn `
    --handler $handlerName `
    --timeout 120 `
    --zip-file fileb://lambda-cli.zip `
    --region $Region

# Lambda 関数の確認
aws lambda get-function --function-name $FunctionName

# 補足:Lambda 関数を更新
aws lambda update-function-code `
    --function-name $FunctionName `
    --zip-file fileb://lambda-cli.zip `
    --publish

2.3 Lambda 関数のテスト実行

GUI でテスト実行をしてもよいですが、ここでは AWS CLI で実行します。

# Lambda 関数のテスト実行
aws lambda invoke --invocation-type Event `
    --function-name $FunctionName `
    --region $Region `
    --log-type Tail outfile.txt

CloudWatch Logs に次のようなログが出力されていれば、OK です。

起動
{
"AssociationId": "cvpn-assoc-XXXXXXXXXXXXXXXX",
"Status": {
"Code": "associating"
}
}
The button is clicked once.
停止
{
"AssociationId": "cvpn-assoc-XXXXXXXXXXXXXXXX",
"Status": {
"Code": "disassociating"
}
}
The button is double clicked.

3. AWS IoT 1-Click に登録する

こちらの記事ステップ 1: AWS IoT 1-Clickに登録するを参考にしてください。

4. AWS IoT 1-Click のプロジェクトを作成する

AWS管理コンソールから AWS IoT 1-Click のコンソールを開き、以下の画面ショットの通り入力していきます。

image.png
image.png
image.png
image.png
image.png

登録したデバイスを選択し、プレイスメントの作成を完了すれば、設定は全て完了です。
試しに、IoTデバイスのボタンをクリックして、AWS IoT 1-Click のコンソールのレポートから Lambda関数呼び出しに成功したことを確認できれば合格です。
お疲れ様でした。
image.png

最後に

前の記事 節約術!? AWS Client VPN を Lambda で定時に起動・停止してみた で、定時に Client VPN エンドポイントの関連付け・関連付け解除をする方法を紹介しました。
今回は以前から気になっていたIoTボタンと連携して、関連付け・関連付け解除をできるようにしてみました。
定時実行よりも融通がききますし、ボタンをクリックしたら、よし!開発するぞっというやる気スイッチも入るのでおすすめです(笑)。

最後まで読んでいただきまして、ありがとうございました。

speaktech
AWSが好きなインフラ屋さんです。 検証結果や作ったものを紹介していきます。 LGTMをいただけると、励みになります。記事内容にご意見・ご質問等があれば、全て回答しますのでコメントをお寄せください!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした