MyGPTsでAWS操作用のGPTを作成してみた
こんにちは。しろまです。
ChatGPTには、自分専用にGPTをカスタマイズできるMyGPTsという機能があります。
今回は、そのMyGPTsを使って、AWSを操作するGPTを作成したので、備忘録的な感じで記事にしてみました。
MyGPTsとAWSの連携について
AWSの操作の多くは、AWSマネジメントコンソールへログインし、GUIで操作しますが、AWS SDK for Python (Boto3)ライブラリを使用すれば、pythonのプログラムで、ほとんどの操作が可能になります。
今回は、このBoto3をサーバーレス環境である AWS Lambda 上で動作させ、MyGPTsとAPIで連携させることでMyGPTsのプロンプトからの指示でAWSを操作できるようにしてみました。
また、ChatGPTのコードインタープリタ機能は、そもそもpythonコードを実行できるので、「AWS LambdaやAPI Gateway は要らないのでは?」
と思う方もいると思うので、補足ですが、コードインタープリタ機能には制限があり、現在はChatGPT内での実行にのみ対応しており、直接の外部アプリケーションやサービスとの接続は不可で、ライブラリを追加インストールすることもできないため、Boto3 を使用することができません。
そのため、Boto3 を外部で実行させAPI連携させる必要があるため、前述のやり方になりました。
ではさっそく、作成したMyGPTsを動かしてみます。
作成したMyGPTsの動作
まずは、「Linuxサーバを1台作成して」と指示をしてみます。
指示を出して数秒で、1台作成できました。
パブリックIPアドレスについては、割り当てまでに多少時間がかかるのでPending になっています。
「パブリックIPアドレスが割り当てられたら表示して」と指示を出すと、インスタンス情報が表示されました。
AWSマネジメントコンソール上でも確認。
該当のインスタンスがちゃんと作成できています。
削除します。
今度は、Linuxサーバを複数台作成してみます。
「Linuxサーバを10台作成して」と指示を出します。
作成できました。こちらも数秒で完了します。
AWSマネジメントコンソールでも確認。
ちゃんと作成されてます。
今度は、先月の請求金額を聞いてみます。
金額が細かくでちゃいました。
AWSマネジメントコンソールでは、丸められて0.5 USD になってました。
指示を出せば、数字を丸めて表示することもできます。
では、設定のほうを記載していきます。
AWS側の設定
(大まかな流れです)
① IAMユーザの作成とポリシーの適用
今回は、EC2インスタンスの作成や削除などの操作と、請求金額を読み取れるようにするため、最低でも下記のポリシーを適用させる
・AmazonEC2FullAccess
・AWSBillingReadOnlyAccess
※必要に応じて AdministratorAccess(すべての操作を行う場合)
② キーペアの作成
SSH接続や、RDP接続の際に使用する
③ セキュリティグループの作成
SSH(22)や、RDP(3389)を許可する
④ AWS Lambdaの設定
下記をLambda関数へ記載しデプロイ
・キーペア名
・AMI ID
・セキュリティグループID
Lambda関数
import json
import boto3
import datetime
import os
from typing import Any, Dict
# 環境変数から設定を取得
REGION_EC2 = os.getenv('REGION_EC2', 'ap-northeast-1')
REGION_CE = os.getenv('REGION_CE', 'us-east-1')
KEY_NAME = os.getenv('KEY_NAME', 'your-key-pair') # 実際のキーペア名を記載
AMI_ID = os.getenv('AMI_ID', 'ami-xxxxxxxxxxxx') # 該当のAMIIDを記載
SECURITY_GROUP_ID = os.getenv('SECURITY_GROUP_ID', 'sg-xxxxxxxxxxxx') # セキュリティグループIDを記載
# クライアントの初期化
ec2 = boto3.client('ec2', region_name=REGION_EC2)
ce = boto3.client('ce', region_name=REGION_CE)
# 定数定義
ERROR_MISSING_COUNT = 'Count parameter is missing'
ERROR_INVALID_COUNT = 'Invalid count parameter. Must be an integer'
ERROR_MISSING_INSTANCE_ID = 'Instance ID is missing'
ERROR_INVALID_OPERATION = 'Invalid operation'
def create_instance(event: Dict[str, Any]) -> Dict[str, Any]:
"""指定された数のEC2インスタンスを作成します。"""
count = event.get('count')
if count is None:
return error_response(400, ERROR_MISSING_COUNT)
try:
instance_count = int(count)
except ValueError:
return error_response(400, ERROR_INVALID_COUNT)
try:
response = ec2.run_instances(
ImageId=AMI_ID,
InstanceType='t2.micro',
KeyName=KEY_NAME,
MinCount=instance_count,
MaxCount=instance_count,
SecurityGroupIds=[SECURITY_GROUP_ID],
)
instances = [{'InstanceId': instance['InstanceId'], 'PublicIpAddress': instance.get('PublicIpAddress', 'Pending')} for instance in response['Instances']]
return success_response({'message': 'EC2 instances created successfully', 'instances': instances, 'KeyName': KEY_NAME})
except boto3.exceptions.Boto3Error as e:
return error_response(500, f'Boto3 error: {str(e)}')
except Exception as e:
return error_response(500, str(e))
def get_instance_info(event: Dict[str, Any]) -> Dict[str, Any]:
"""指定されたインスタンスの情報を取得します。"""
instance_id = event.get('instance_id')
if instance_id is None:
return error_response(400, ERROR_MISSING_INSTANCE_ID)
try:
response = ec2.describe_instances(InstanceIds=[instance_id])
instance = response['Reservations'][0]['Instances'][0]
return success_response({'InstanceId': instance_id, 'PublicIpAddress': instance.get('PublicIpAddress'), 'KeyName': instance.get('KeyName')})
except boto3.exceptions.Boto3Error as e:
return error_response(500, f'Boto3 error: {str(e)}')
except Exception as e:
return error_response(500, str(e))
def get_last_month_billing(event: Dict[str, Any]) -> Dict[str, Any]:
"""先月の請求金額を取得します。"""
try:
end = datetime.datetime.now()
start = end - datetime.timedelta(days=30)
response = ce.get_cost_and_usage(
TimePeriod={'Start': start.strftime('%Y-%m-%d'), 'End': end.strftime('%Y-%m-%d')},
Granularity='MONTHLY',
Metrics=['UnblendedCost']
)
amount = response['ResultsByTime'][0]['Total']['UnblendedCost']['Amount']
return success_response({'last_month_cost': str(amount), 'currency': 'USD'})
except boto3.exceptions.Boto3Error as e:
return error_response(500, f'Boto3 error: {str(e)}')
except Exception as e:
return error_response(500, str(e))
def stop_instance(event: Dict[str, Any]) -> Dict[str, Any]:
"""指定されたインスタンスを停止します。"""
return modify_instance(event, ec2.stop_instances, 'stopped')
def start_instance(event: Dict[str, Any]) -> Dict[str, Any]:
"""指定されたインスタンスを起動します。"""
return modify_instance(event, ec2.start_instances, 'started')
def terminate_instance(event: Dict[str, Any]) -> Dict[str, Any]:
"""指定されたインスタンスを終了します。"""
return modify_instance(event, ec2.terminate_instances, 'terminated')
def modify_instance(event: Dict[str, Any], action: callable, action_name: str) -> Dict[str, Any]:
"""指定されたアクションをインスタンスに適用します。"""
instance_id = event.get('instance_id')
if instance_id is None:
return error_response(400, ERROR_MISSING_INSTANCE_ID)
try:
action(InstanceIds=[instance_id])
return success_response({'message': f'EC2 instance {instance_id} {action_name} successfully'})
except boto3.exceptions.Boto3Error as e:
return error_response(500, f'Boto3 error: {str(e)}')
except Exception as e:
return error_response(500, str(e))
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
"""受け取ったイベントに基づいて適切な操作を実行します。"""
operation = event.get('operation')
operations = {
'create': create_instance,
'stop': stop_instance,
'start': start_instance,
'terminate': terminate_instance,
'info': get_instance_info,
'billing': get_last_month_billing,
}
if operation in operations:
return operations[operation](event)
else:
return error_response(400, ERROR_INVALID_OPERATION)
def success_response(body: Dict[str, Any]) -> Dict[str, Any]:
"""成功レスポンスを生成します。"""
return {
'statusCode': 200,
'body': json.dumps(body)
}
def error_response(status_code: int, message: str) -> Dict[str, Any]:
"""エラーレスポンスを生成します。"""
return {
'statusCode': status_code,
'body': json.dumps({'error': message})
}
⑤ API Gatewayの作成
作成後、生成されたAPIエンドポイントをのちほどMyGPTsの設定で使用する
MyGPTs側の設定
① 右上のプロフィールアイコンをクリック
② 「MyGPTs」をクリック
③ 「Configure」をクリック
④ MyGPTsの名前を入力
⑤ 「Instructions」 に下記を入力
"manage_ec2.py" ファイルについて
このPythonスクリプトは、EC2インスタンスを管理するためのものです。具体的な操作としては、以下です。
1. EC2インスタンスの作成
2. EC2インスタンスの停止
3. EC2インスタンスの起動
4. EC2インスタンスの削除
5. EC2インスタンスの情報取得
6. 先月の請求金額の取得
⑥「Upload files」をクリックし、下記内容のpythonファイルをアップロード
import requests
import re
API_URL_TEMPLATE = "https://xxxxxxxxxxxxxxxxxxxxxxxx"
HEADERS = {'Content-Type': 'application/json'}
def manage_ec2_instance(operation, payload=None):
url = API_URL_TEMPLATE.format(operation=operation)
try:
response = requests.post(url, json=payload, headers=HEADERS)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}
def extract_value(prompt, pattern):
match = re.search(pattern, prompt)
return match.group(1) if match else None
def handle_prompt(prompt):
if "Linuxサーバを" in prompt and "台作成して" in prompt:
count = extract_value(prompt, r"Linuxサーバを(\d+)台作成して")
if count:
return manage_ec2_instance('create', {"count": int(count)}).get("message", "EC2 instance creation failed")
instance_id = extract_value(prompt, r"ID: (\w+)")
if instance_id:
if "インスタンスを停止して" in prompt:
return manage_ec2_instance('stop', {"instance_id": instance_id}).get("message", "EC2 instance stop failed")
elif "インスタンスを起動して" in prompt:
return manage_ec2_instance('start', {"instance_id": instance_id}).get("message", "EC2 instance start failed")
elif "インスタンスを削除して" in prompt:
return manage_ec2_instance('terminate', {"instance_id": instance_id}).get("message", "EC2 instance termination failed")
elif "のログイン情報を教えて" in prompt:
response = manage_ec2_instance('info', {"instance_id": instance_id})
if 'error' in response:
return response['error']
return (f"インスタンス ID: {response['InstanceId']} のログイン情報:\n"
f"パブリックIPアドレス: {response['PublicIpAddress']}\n"
f"キーペア名: {response['KeyName']}")
if "請求金額を教えて" in prompt:
response = manage_ec2_instance('billing')
if 'error' in response:
return response['error']
return f"先月の請求金額: {response['last_month_cost']} {response['currency']}"
return "不明な指示です"
if __name__ == "__main__":
prompt = input("プロンプトを入力してください: ")
result = handle_prompt(prompt)
print(result)
⑦ 画像生成はしないため、「DALL・E Image Generation」のチェックを外す
⑧ 「Create new action」をクリック
⑨ 下記OpenAPI仕様書に、APIエンドポイントを記載し、「Schema」に入力
openapi: 3.0.0
info:
title: MyGPT API
description: API for managing EC2 instances using Lambda and API Gateway.
version: 1.0.0
servers:
- url: https://xxxxxxxxxxxxxxxxxxxxxxxx # APIエンドポイントを記載する
paths:
/create-instance:
post:
summary: Create EC2 instance
operationId: createEc2Instance
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
count:
type: integer
example: 1
responses:
'200':
description: EC2 instance created successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: EC2 instance created successfully
instances:
type: array
items:
type: object
properties:
InstanceId:
type: string
example: i-0123456789abcdef0
PublicIpAddress:
type: string
example: 203.0.113.0
KeyName:
type: string
example: your-key-pair-name
'500':
description: Error creating EC2 instance
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Error creating EC2 instance
/stop-instance:
post:
summary: Stop EC2 instance
operationId: stopEc2Instance
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
instance_id:
type: string
example: i-0123456789abcdef0
responses:
'200':
description: EC2 instance stopped successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: EC2 instance stopped successfully
'500':
description: Error stopping EC2 instance
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Error stopping EC2 instance
/start-instance:
post:
summary: Start EC2 instance
operationId: startEc2Instance
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
instance_id:
type: string
example: i-0123456789abcdef0
responses:
'200':
description: EC2 instance started successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: EC2 instance started successfully
'500':
description: Error starting EC2 instance
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Error starting EC2 instance
/terminate-instance:
post:
summary: Terminate EC2 instance
operationId: terminateEc2Instance
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
instance_id:
type: string
example: i-0123456789abcdef0
responses:
'200':
description: EC2 instance terminated successfully
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: EC2 instance terminated successfully
'500':
description: Error terminating EC2 instance
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Error terminating EC2 instance
/get-instance-info:
post:
summary: Get EC2 instance information
operationId: getEc2InstanceInfo
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
instance_id:
type: string
example: i-0123456789abcdef0
responses:
'200':
description: EC2 instance information retrieved successfully
content:
application/json:
schema:
type: object
properties:
InstanceId:
type: string
example: i-0123456789abcdef0
PublicIpAddress:
type: string
example: 203.0.113.0
KeyName:
type: string
example: your-key-pair-name
'500':
description: Error retrieving EC2 instance information
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Error retrieving EC2 instance information
/get-last-month-billing:
get:
summary: Get last month's billing information
operationId: getLastMonthBilling
responses:
'200':
description: Last month's billing information retrieved successfully
content:
application/json:
schema:
type: object
properties:
last_month_cost:
type: string
example: "123.45"
currency:
type: string
example: "USD"
'500':
description: Error retrieving billing information
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: Error retrieving billing information
設定は以上になります。
まとめ
今回作成したMyGPTsは、手始めとして、EC2インスタンスの基本的な操作と請求金額の確認のみですが、設定次第ではもっと色々なことができます。
個人的には、できることをもっと追加していこうと思っています。
AWSには200種類以上のサービスがあり、それぞれが独自のサービス名であったり、設定や操作方法も独特なところがあり、なかなか習得できないかたもいると思います。
今回のようなMyGPTsを作成して連携してしまえば、チャット形式で操作ができるので、AWSの知識がない方でも、容易に操作ができるようになります。
また、AWSの学習にも活用できますので、用途は様々広がると思いますので、ぜひ機会があれば試してみてくださいね!
著者: