こんにちは、クラウドエンジニアの浅野です。
正直に白状すると、私はSNSの「定期的な手動投稿」が非常に苦手です。
しかし、昨今のX(旧Twitter)を取り巻くAPI仕様の激変やサードパーティ製ツールの制限を考えると、自前の発信基盤を持つ重要性はかつてないほど高まっています。
エンジニアとしては、この課題を「時間をかけずに、かつ自分の技術」でスマートに解決したいもの。今回は「あえて時間をかけずにお手軽に」をコンセプトに、AWSのマネージドサービスを組み合わせて構築した自動投稿システムについてまとめます。
1. システム構成:マネージドサービスで「楽」をする
今回の構成は、運用コストほぼゼロ・構築時間最短を狙った「サーバーレス・ファースト」な設計です。他にもやり方は色々ありますが、個人のツールに時間をかけすぎるのは本質的ではないため、以下の最短ルートを選びました。
- EventBridge Scheduler: タイムゾーン(Asia/Tokyo)を直接指定。UTC計算すら不要なお手軽設定。
- AWS Lambda (Python 3.14): 最新ランタイムを採用。処理は最小限のロジックに特化。
- AWS Secrets Manager: APIキーを安全に一元管理。環境変数へのハードコードを排除します。
- CloudWatch Alarms: Lambdaの実行失敗だけでなく、EventBridge Schedulerの起動失敗も監視対象。

2. 実装のポイント:CloudFormationによる「一撃デプロイ」
環境構築に手間をかけないよう、CloudFormation (CFn) を使い、インフラを一気に立ち上げました。
① EventBridge Scheduler の権限(IAM Role)
SchedulerがLambdaをキックするためには、Schedulerサービス(scheduler.amazonaws.com)に対する信頼関係(AssumeRole)が必要です。
SchedulerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: scheduler.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: InvokeLambdaPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: lambda:InvokeFunction
Resource: !GetAtt XPosterFunction.Arn # 特定のリソースARNへの最小権限
② スケジュール定義とターゲット入力
Scheduler側の「ターゲット入力」を活用し、朝と夜でメッセージを出し分けます。このように、「設定側(AWS側)で解決し、コードを減らす」のが、実装時間を短縮するプロのコツです。
# 毎日 9:00 JST に実行するスケジュールの例
MorningSchedule:
Type: AWS::Scheduler::Schedule
Properties:
ScheduleExpression: 'cron(0 9 * * ? *)'
ScheduleExpressionTimezone: 'Asia/Tokyo' # JST指定!
Target:
Arn: !GetAtt XPosterFunction.Arn
RoleArn: !GetAtt SchedulerRole.Arn
Input: '{"time_period": "morning"}' # コード側で判定するためのパラメータ
3. Lambda 実装:Python 3.14 による最小限のロジック
Lambda側では、Secrets Managerから認証情報を取得し、10パターンの本文をランダムに回す仕様です。
import json
import random
import boto3
import os
import tweepy
def lambda_handler(event, context):
# 1. Secrets ManagerからAPIキーを取得
sm_client = boto3.client("secretsmanager")
secret_value = sm_client.get_secret_value(SecretId=os.environ['X_API_SECRETS'])
secrets = json.loads(secret_value['SecretString'])
# 2. 投稿メッセージの組み立て
period = event.get('time_period', 'morning')
greeting = "おはようございます!" if period == 'morning' else "こんばんは!"
bodies = ["経歴について...", "技術スタックについて...", "理念について..."] # ここに10パターン用意
message = f"{greeting}\n\n{random.choice(bodies)}\n\n#AWS #エンジニア"
# 3. X API v2で投稿
client = tweepy.Client(
consumer_key=secrets['API_KEY'],
consumer_secret=secrets['API_SECRET'],
access_token=secrets['ACCESS_TOKEN'],
access_token_secret=secrets['ACCESS_TOKEN_SECRET']
)
try:
client.create_tweet(text=message)
except Exception as e:
print(f"Error: {e}")
raise e # CloudWatchアラーム検知のため例外を再送出
4. 実装中に直面した「リアルな罠」
① X API の「無料枠の終焉」と課金形態の変化
最大の壁となったのは、コードではなくX APIの絶え間ない仕様変更でした。
以前のような無料枠(Freeティア)は実用的な運用においては実質的に機能しなくなっています。現在は、月額プランへの加入、もしくは必要な分だけクレジットをチャージする「Pay-Per-Use(従量課金)」モデルの利用が前提となります。
また、アプリの権限設定をデフォルトの「Read」から 「Read and Write」 に変更しないと投稿が即座に拒否されるため、Developer Portal側の設定確認も不可欠です。
② CloudShell でのパッケージングエラー
ライブラリ(Tweepy)を Lambda Layer 化する際、CloudShell上で pip3.14 コマンドが認識されず、空のZIPを作成してしまう事象(Uploaded file must be a non-empty zip)に遭遇しました。
これは python3 -m pip install を使い、以下のディレクトリ構造を正確に守ることで解決しました。
# 正しいLayerディレクトリ構造
layer/
└─ python/
└─ lib/
└─ python3.14/
└─ site-packages/ (ここにインストール)
5. 運用:エラーが出たら気付くだけでいい
毎日2回の投稿。失敗しても死ぬわけではありませんが、一応 CloudWatch Alarms を設定。
Lambdaの実行失敗だけでなく、EventBridge Schedulerが正常にLambdaを呼び出せたか(Invocation errors)も監視対象に含め、何かあればすぐに気づける体制にしています。
6. おわりに
今回の自動化により、インフラエンジニアとして「自らの手を動かして仕組みで解決する」ことを体現できました。
「KIT」の理念の通り、確かな Technology を提供しつつ、皆様との Keep In Touch(繋がり) をこれからも大切にしていきたいと思います。
クラウド導入や運用の自動化、AWS Organizations等によるマルチクラウド環境の最適化など、お困りの際はお気軽に お問い合わせください!
【公開にあたっての注記】
- 認証情報について:記事中のコードやスニペットは説明用のサンプルです。実運用ではAPIキーやトークンをコードに含めず、Secrets Managerなどで安全に管理してください。
- CloudFormationについて:実際のテンプレートを公開・利用する際は、AWSアカウントIDやリソースARNをプレースホルダに置き換え、IAMポリシーは最小権限に調整してください。
- X APIについて:最新の料金体系、レート制限、権限設定は随時変更されます。運用前には必ず X Developer Platform で最新情報を確認してください。