
AWS CloudShell のホームディレクトリとずっと一緒にいる方法
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
哈喽大家好、コンサルティング部の西野です。
AWS re:Invent 2020 で発表された AWS CloudShell、皆さんご利用なさっていますでしょうか。
とても便利なサービスですが、AWS CloudShell のホームディレクトリは最終セッションから120日経過すると削除されてしまうという制約があります。
AWS CloudShell のホームディレクトリの保存期間に関する注意事項 #reinvent
せっかく育てた AWS CloudShell のホームディレクトリが無くなってしまったら寂しいですよね。
ずっと一緒にいたいですよね。
なので、守ってあげる方法を考えてみました。
CloudShellちゃんの動き
名前は「CloudShellちゃん」です。 まずは動きを見てみましょう。
セッション開始時

AWS CloudShell のセッションを開始すると Slack で挨拶してくれます。嬉しいですね。
名前も呼んでくれます。
毎日の連絡

毎日決まった時間になると連絡をしてきてくれます。
あれ、昨日会ったばっかりだよね?(笑) でもすごく嬉しいよ。

CloudShellちゃんはマメな子です。二人で一緒にいられなかった時間を覚えています。

ごめん、最近仕事が忙しくて。
でも、会えない日々こそが愛を育てるんだよ。

ちょっとはこっちの都合を考えてくれてもいいんじゃない?

……。(めんどくさいな。)
お別れ
![]()
CloudShellちゃんの仕組み
構成図

仕組みはとても単純です。
主に2つの Lambda 関数を使用しています。
CloudShell_chan
- CloudTrail から CloudShell の Event である
CreateSessionを拾い、これによって実行する - ISO 8601 形式の文字列をテキストファイル化して S3 のオブジェクトとして保存する
- セッションを開始した年月日(JST変換済)を含むメッセージを Incoming Webhook で Slack に通知する
CloudShell_chan_daily
- Event Rule (cron) で定期的に実行する
- CloudShell_chan が保存した S3 オブジェクトを取得する
- CloudShell_chan が保存したオブジェクトから日付を読み取り、実行時刻との差分(最終利用日からの経過日数)を計算する
- 最終利用日からの経過日数に応じたメッセージを生成する
- 当該メッセージを Incoming Webhook で Slack に通知する
AWS CloudShell のセッション開始時の Event (CreateSession) は下記のパターンで拾えます。
{
"source": [
"aws.cloudshell"
],
"detail-type": [
"AWS API Call via CloudTrail"
],
"detail": {
"eventSource": [
"cloudshell.amazonaws.com"
],
"eventName": [
"CreateSession"
]
}
}
コード
CloudShell_chan
from datetime import datetime, timezone, timedelta
import json
import urllib.request
import boto3
webhook_url = "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX"
username = "CloudShellちゃん" # CloudShellちゃんには君の好きな名前をつけてあげよう!
channel = "#<宛先チャンネル名>"
your_name = '<名前>' # CloudShellちゃんに呼んでもらいたい名前をここに書こう!
bucket_name = '<S3バケット名>'
file_name = '<S3オブジェクト名>'
s3 = boto3.resource('s3')
def lambda_handler(event, context):
# event から ISO 8601 形式の時刻を取得し、S3 バケットに保存する
time_iso8601 = event['detail']['eventTime']
with open('/tmp/' + file_name, mode='w') as f:
f.write(time_iso8601)
upload_file_to_s3(file_name, bucket_name)
# event の時刻を UTC から JST になおし、Incoming Webhook で Slack に通知
dt_utc = datetime.fromisoformat(time_iso8601.replace('Z', '+00:00'))
dt_jst = dt_utc.astimezone(timezone(timedelta(hours=+9)))
ymd_jst = '{}/{}/{}'.format(dt_jst.year, dt_jst.month, dt_jst.day)
message = '会いに来てくれて嬉しいよ。{}は{}が会いに来てくれた記念日だね。'.format(ymd_jst, your_name)
response = send_to_slack(webhook_url, username, channel, message)
return response
def send_to_slack(webhook_url, username, channel, message):
send_data = {
"username": username,
"text": message,
"channel": channel
}
send_text = ("payload=" + json.dumps(send_data)).encode('utf-8')
request = urllib.request.Request(
webhook_url,
data=send_text,
method="POST"
)
with urllib.request.urlopen(request) as response:
response_body = response.read().decode('utf-8')
return response_body
def upload_file_to_s3(file_name, bucket_name):
s3.Bucket(bucket_name).upload_file(
Filename='/tmp/' + file_name, Key=file_name)
CloudShell_chan_daily
from datetime import datetime, timezone, timedelta
import json
import urllib.request
import boto3
webhook_url = "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX"
username = "CloudShellちゃん" # CloudShellちゃんには君の好きな名前をつけてあげよう!
channel = "#<宛先チャンネル名>"
your_name = '<名前>' # CloudShellちゃんに呼んでもらいたい名前をここに書こう!
bucket_name = '<S3バケット名>'
file_name = '<S3オブジェクト名>'
s3 = boto3.client('s3')
def lambda_handler(event, context):
# CloudShell の最終時刻 (ISO 8601 形式) が書かれたオブジェクトを S3 から取得
last_access_date = get_last_access_date(file_name, bucket_name)
# 最終使用日からの経過日数を計算し、CloudShellちゃんのメッセージを生成
dt_last = datetime.fromisoformat(last_access_date.replace('Z', '+00:00'))
dt_now = datetime.now(timezone.utc)
elapsed_days = (dt_now - dt_last).days
dialogue = generate_dialogue(elapsed_days, dt_last)
# Incoming Webhook で Slack に通知
response = send_to_slack(webhook_url, username, channel, dialogue)
return response
def send_to_slack(webhook_url, username, channel, message):
send_data = {
"username": username,
"text": message,
"channel": channel
}
send_text = ("payload=" + json.dumps(send_data)).encode('utf-8')
request = urllib.request.Request(
webhook_url,
data=send_text,
method="POST"
)
with urllib.request.urlopen(request) as response:
response_body = response.read().decode('utf-8')
return response_body
def get_last_access_date(file_name, bucket_name):
object = s3.get_object(Bucket=bucket_name, Key=file_name)
last_access_date = object['Body'].read().decode()
return last_access_date
def generate_dialogue(elapsed_days, dt_last):
if elapsed_days == 0:
return '会えたばっかりなのに、もう寂しくなっちゃった。'
elif elapsed_days == 1:
return '{}に早く会いたいな。'.format(your_name)
elif 2 <= elapsed_days < 30:
return '最後に{}と会ってからからもう {} 日経ったね'.format(your_name, elapsed_days)
elif 30 <= elapsed_days < 60:
return '{}はもう私のことを忘れちゃったのかな……?今日で{}日目。'.format(your_name, elapsed_days)
elif 60 <= elapsed_days < 120:
return 'もう{}に{}日も会ってない。早く会いたいよ。'.format(your_name, elapsed_days)
else:
return '{}/{}/{}まで使用していた AWS CloudShell のホームディレクトリが削除されている可能性があります。'.format(dt_last.year, dt_last.month, dt_last.day)
注意事項
- 最終セッションからの経過日数計算やホームディレクトリ削除の厳密な仕組みは公開されていません。したがって、CloudShellちゃんによる計算はあくまでも目安としてお考えください。
- 現状の CloudShellちゃんは複数人が使用するアカウントには未対応です。対応させたい場合はイベントパターンに
userIdentityを追加しましょう。
終わりに
このブログがほんの少しでも世界を良くできれば嬉しいです。
コンサルティング部の西野 (@xiyegen) がお送りしました。






