AWSのVPNエンドポイントって、知らない間にお金かかりますね。知らずに放っておいたら、高額の請求が来てギャフンとなりました。何か対策を打たなくてはなりません。使わない時は、VPNエンドポイントとサブネットとの関連付けを解除、使う時だけ作成すればよいのですが、それをAWSのコンソールにいちいち入って、作成・解除するのは面倒です。そこで、以下の記事を参考に、
AWS Client VPN節約術!~AWS Lambdaで関連付け自動化~
そして、Anthlopic Clude3 Sonnetの力も借りながらPythonスクリプトを作ってみました。なお使う前に
pip install boto3
をお忘れなく。
AWS Client VPN では、アクティブなクライアント接続の数に対して、および Client VPN に関連付けられているサブネットの数に対してそれぞれ 1 時間単位の料金が発生します。
料金 - AWS VPN | AWS
とあるように、関連付けられているサブネットの数に対して時間単位で課金され、OpenVPN等で接続している間も課金されるわけです。使っていない間は、VPNエンドポイントとサブネットの関連付けを解除することで料金を節約できます。
VPNエンドポイント関連付け作成
こんな感じのスクリプトが出来上がりました。スクリプト内に、クライエントVPNエンドポイントIDとサブネットIDを記述する部分があります。AWSコンソールでIDを調べておきましょう。なお、AWSをターミナルから制御する際に権限が必要になりますので、それを設定する必要があります(後述)。
#
# VPNエンドポイントとサブネット関連付けを設定する
#
import boto3
import time
client = boto3.client(
'ec2',
region_name='ap-northeast-3',
#本当は直接アクセスキーを記述しない方が良いのですが
aws_access_key_id='xxxxxxxxxxxxxxxxx', #AWSアクセスキーを記述
aws_secret_access_key='YYYYYYYYYYYYYYYYY' #AWSシークレットアクセスキーを記述
)
def wait_for_association(vpn_endpoint_id, association_id):
while True:
response = client.describe_client_vpn_target_networks(
ClientVpnEndpointId=vpn_endpoint_id,
AssociationIds=[association_id]
)
state = response['ClientVpnTargetNetworks'][0]['Status']['Code']
if (state == 'available') or (state == 'associated') :
print("Association is now available")
break
elif state == 'pending-associate':
print("Association is still pending, waiting...")
time.sleep(30) # wait for 30 seconds before checking again
elif state == 'associating':
print("Associating now, waiting...")
time.sleep(30) # wait for 30 seconds before checking again
else:
raise Exception(f"Unexpected state: {state}")
clientVpnEndpointId = 'cvpn-xxxxxxxxxxxxxxxx' #クライアントVPNエンドポイントのIDを記述
subnetId = 'subnet-xxxxxxxxxxxxxxx' #関連付けしたいサブネットIDを記述
response = client.associate_client_vpn_target_network(
ClientVpnEndpointId = clientVpnEndpointId,
SubnetId = subnetId
)
association_id = response['AssociationId']
wait_for_association(clientVpnEndpointId,association_id)
client.close()
VPNエンドポイント関連付け解除
同じく、今度は解除する時には知らせるスクリプトです。スクリプト内に、クライエントVPNエンドポイントIDとサブネットIDを記述する部分があります。また例によって、AWSをターミナルから制御する際に権限が必要になりますので、それを設定する必要があります。
#
# VPNエンドポイントとサブネットの関連付けを解除する
#
import boto3
client = boto3.client(
'ec2',
region_name='ap-northeast-3',
#本当は直接アクセスキーを記述しない方がいいのですが
aws_access_key_id='xxxxxxxxxxxxxxxxx',#AWSアクセスキーを記述
aws_secret_access_key='YYYYYYYYYYYYYYYYYYYY'#AWSシークレットアクセスキーを記述
)
clientVPNEndpointID = 'cvpn-endpoint-xxxxxxxxxxxx' #ここにエンドポイントIDを記述
ClientVpnData = client.describe_client_vpn_target_networks(
ClientVpnEndpointId = clientVPNEndpointID
)
AssociationId = ClientVpnData['ClientVpnTargetNetworks'][0]['AssociationId']
client.disassociate_client_vpn_target_network(
ClientVpnEndpointId = clientVPNEndpointID,
AssociationId = AssociationId
)
IAMで必要な権限を設定
(1) まず、IAM(Identity and Access Management)のIAM>ユーザー
にてVPNのエンドポイントを操作する専用のアカウントを作っておきます。今回はVPN_Controller
という名前にしておきました。
(2) 次に、アクセスキーを作成し、アクセスキーとシークレットアクセスキーを記録しておきます。
(3) VPN_Controller
にアタッチする専用のポリシーを作ります。今回はVPN_Control
という名前にしました。以下のJSONを丸々貼り付ける様にすれば大丈夫なはずです。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:AssociateClientVpnTargetNetwork",
"ec2:DisassociateClientVpnTargetNetwork",
"ec2:DescribeClientVpnTargetNetworks",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs"
],
"Resource": "*"
}
]
}
権限については以下の通りです。(参照「Amazon のアクション、リソース、および条件キー EC2」)
AssociateClientVpnTargetNetwork:ターゲットネットワークをクライアントVPNエンドポイントに関連付けるアクセス許可を付与します。
DisassociateClientVpnTargetNetwork:クライアントVPNエンドポイントからターゲットネットワークの関連付けを解除する許可を付与します。
DescribeClientVpnTargetNetworks:クライアントVPNエンドポイントに関連付けられているターゲットネットワークを記述するアクセス許可を付与します。
DescribeSubnets:1 つ以上のサブネットを記述する許可を付与します。
DescribeVpcs:1 つ以上の VPC を記述するアクセス許可を付与します。
(4) ユーザーVPN_Controller
に許可ポリシーVPN_Control
をアタッチします。
(5) アクセスキー、シークレットアクセスキーを設定します。
使ってみる
VPN_Start.py
は実際にVPNエンドポイントが使えるようになるまで、Associating now, waiting...
と出力しながら待ってくれます。
$ python VPN_Start.py
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Associating now, waiting...
Association is now available
VPN_End.py
の方は、実行したらすぐに終了するスクリプトです。
(2024/9/3追記)それにしても、VPNエンドポイントとサブネットとの関連付けの作成、解除は結構完了するのに時間がかかりますね。どうしてでしょう?