今回はAmazon Bedrockのプレイグラウンドから構成図を読み込ませ、IaCファイルが生成できないかを試しました。
背景
Amazon Bedrockは複数の複数の生成AIのFoundation Modelを提供するサービスです。Amazon BedrockはPlaygroundというコンソールも提供しており、ChatGPT / Claudeのように使うこともできます。
今回はAmazon BedrockのPlaygroundからAWSの構成図を渡し、CloudFormationの定義ファイルを生成できないか試してみました。
※参考:
- ChatGPTでAWS CloudFormationテンプレートを作成してみた|クラウドテクノロジーブログ|ソフトバンク
- AWSの構成図をChatGPT(GPT-4V)に読み込ませてIaCコードを生成してみた | DevelopersIO
- GPT4-Vで構成図からIaCコードを生成させる #OpenAI - Qiita
- 生成 AI をインフラのタスクに活用してみた -
検証
Amazon BedrockはClaude 3 Sonnet/Opusをモデルとして使用しました。
また入力する構成図はAWSの提供するこちらから2つを使いました。
Amazon Bedrockのモデル有効化
なおAmazon Bedrockはリージョンによって使えるモデルが限られており、今回はオレゴン (us-west-2
)を使用しました。
Amazon BedrockでClaude 3モデルを利用するため、最初にモデルの有効化を行います。Amazon Bedrockのメニューに移動し、 モデルアクセス
を選択します。画面遷移後、モデルアクセスを管理
を選択します。
ここから利用するモデルを選択します。Claudeを使うには、最初にユースケースの詳細を送信
を選択します。
色々聞かれるので入力・送信します。
しばらくするとClaudeもリクエスト可能となります。
Claudeモデルを選択し、画面下部のモデルアクセスをリクエスト
を選択します。
利用可能になるまでしばらく待ちます。
アクセスが付与されました
と出たら利用可能です。
構成図を渡してCloudFormationを生成する
PlaygroundからClaudeを利用するため、画面左メニューからチャット
画面に移動します。画面遷移後はまず モデルを選択
を選択します。
モデルの選択画面では カテゴリ
と モデル
を選択します。
チャットなどが入力可能となります。
今回は以下のようなプロンプトを利用しました。色々と作り込むことはできそうですが、シンプルにしてます。
出力結果は以下の通りです。
Claude 3 Sonnet: Webサイト
はい、この構成図に基づいてCloudFormationテンプレートを生成することができます。ここでは、VPC、サブネット、ロードバランサー、EC2インスタンス、RDSインスタンスなどの主要リソースを定義する必要があります。
Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: MyVPC InternetGateway: Type: AWS::EC2::InternetGateway PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ''] CidrBlock: 10.0.1.0/24 MapPublicIpOnLaunch: true PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [1, !GetAZs ''] CidrBlock: 10.0.2.0/24 MapPublicIpOnLaunch: true PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ''] CidrBlock: 10.0.3.0/24 PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [1, !GetAZs ''] CidrBlock: 10.0.4.0/24 NatGateway1: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGatewayEIP1.AllocationId SubnetId: !Ref PublicSubnet1 NatGatewayEIP1: Type: AWS::EC2::EIP Properties: Domain: vpc NatGateway2: Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatGatewayEIP2.AllocationId SubnetId: !Ref PublicSubnet2 NatGatewayEIP2: Type: AWS::EC2::EIP Properties: Domain: vpc PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PrivateRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PrivateRouteTable2: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC PublicRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PrivateRoute1: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 PrivateRoute2: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PrivateRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref PrivateRouteTable1 PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref PrivateRouteTable2 WebServerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: WebServerSG GroupDescription: Allow HTTP/HTTPS traffic VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 WebServerInstance1: Type: AWS::EC2::Instance Properties: ImageId: ami-0cff7528ff583bf9a InstanceType: t2.micro KeyName: mykey NetworkInterfaces: - AssociatePublicIpAddress: "true" DeviceIndex: "0" GroupSet: - !Ref WebServerSecurityGroup SubnetId: !Ref PublicSubnet1 WebServerInstance2: Type: AWS::EC2::Instance Properties: ImageId: ami-0cff7528ff583bf9a InstanceType: t2.micro KeyName: mykey NetworkInterfaces: - AssociatePublicIpAddress: "true" DeviceIndex: "0" GroupSet: - !Ref WebServerSecurityGroup SubnetId: !Ref PublicSubnet2 ELBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: ELBSecurityGroup GroupDescription: Allow HTTP/HTTPS traffic VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 WebServerClassicELB: Type: AWS::ElasticLoadBalancing::LoadBalancer Properties: Subnets: - !Ref PublicSubnet1 - !Ref PublicSubnet2 SecurityGroups: - !Ref ELBSecurityGroup Listeners: - LoadBalancerPort: 80 InstancePort: 80 Protocol: HTTP HealthCheck: Target: HTTP:80/ HealthyThreshold: 3 UnhealthyThreshold: 5 Interval: 30 Timeout: 5 Instances: - !Ref WebServerInstance1 - !Ref WebServerInstance2 DBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: DBSecurityGroup GroupDescription: Allow inbound from web servers VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 3306 ToPort: 3306 SourceSecurityGroupId: !Ref WebServerSecurityGroup DBInstance1: Type: AWS::RDS::DBInstance Properties: DBName: mydb Engine: mysql MasterUsername: root MasterUserPassword: rootpassword DBInstanceClass: db.t2.micro AllocatedStorage: 20 PubliclyAccessible: false VPCSecurityGroups: - !Ref DBSecurityGroup DBSubnetGroupName: !Ref DBSubnetGroup DBInstance2: Type: AWS::RDS::DBInstance Properties: DBName: mydb Engine: mysql MasterUsername: root MasterUserPassword: rootpassword DBInstanceClass: db.t2.micro AllocatedStorage: 20 PubliclyAccessible: false VPCSecurityGroups: - !Ref DBSecurityGroup DBSubnetGroupName: !Ref DBSubnetGroup DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnets available for the RDS SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2
このテンプレートでは、VPC、パブリックサブネット、プライベートサブネット、ネートゲートウェイ、ルートテーブル、セキュリティグループ、EC2インスタンス、ElasticLoadBalancing、RDSインスタンス、DBサブネットグループを定義しています。画像の構成図に合わせて、適切なリソース間の関連付けも行っています。
必要に応じて、AMIIDやインスタンスタイプ、ポート番号、CIDRブロックなどの値を調整する必要があります。また、WAFやCertificate Managerについては、この構成図には含まれていないので追加する必要があります。
Claude 3 Sonnet: イベント駆動
この構成図に基づいて、以下のようなCloudFormationテンプレートを作成できます。
Resources: SourceBucket: Type: AWS::S3::Bucket Properties: BucketName: source-image-bucket DestinationBucket: Type: AWS::S3::Bucket Properties: BucketName: processed-image-bucket ThumbnailFunction: Type: AWS::Lambda::Function Properties: Handler: index.handler Runtime: nodejs12.x Code: ZipFile: | // Node.js コードをここに記述 // Amazon S3 からイメージを取得し、 // サムネイルを生成し、別の S3 バケットに保存 Role: !GetAtt LambdaRole.Arn Environment: Variables: SOURCE_BUCKET: !Ref SourceBucket DEST_BUCKET: !Ref DestinationBucket LambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - "sts:AssumeRole" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess - arn:aws:iam::aws:policy/AmazonS3WriteOnlyAccess PermissionForBucket: Type: AWS::Lambda::Permission Properties: FunctionName: !GetAtt ThumbnailFunction.Arn Action: "lambda:InvokeFunction" Principal: s3.amazonaws.com SourceAccount: !Ref AWS::AccountId SourceArn: !GetAtt SourceBucket.Arn ThumbnailFunctionTrigger: Type: AWS::S3::Bucket Properties: BucketName: !Ref SourceBucket NotificationConfiguration: LambdaConfigurations: - Event: s3:ObjectCreated:* Function: !GetAtt ThumbnailFunction.Arn
このテンプレートでは、以下のリソースを定義しています。
SourceBucket
: 元の画像ファイルを格納する S3 バケットDestinationBucket
: サムネイル画像を格納する S3 バケットThumbnailFunction
: サムネイル生成の Lambda 関数LambdaRole
: Lambda 関数の実行ロールPermissionForBucket
: S3 がLambda関数を呼び出すためのアクセス許可ThumbnailFunctionTrigger
: SourceBucketにファイルがアップロードされた時にLambda関数をトリガーする設定
Lambda 関数のコードは ZipFile プロパティ内に直接記述するか、別のファイルから読み込む必要があります。
この構成では、画像ファイルが SourceBucket にアップロードされると、Lambda 関数がトリガーされ、そのファイルに対してサムネイル生成処理を行い、生成したサムネイル画像を DestinationBucket に保存します。
Claude 3 Opus: Webサイト
AWSTemplateFormatVersion: '2010-09-09' Description: 'AWS architecture diagram' Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: '10.0.0.0/16' EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: 'Main VPC' InternetGateway: Type: AWS::EC2::InternetGateway VPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway PublicSubnet: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ''] CidrBlock: '10.0.1.0/24' MapPublicIpOnLaunch: true Tags: - Key: Name Value: 'Public Subnet' PrivateSubnet: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [1, !GetAZs ''] CidrBlock: '10.0.2.0/24' MapPublicIpOnLaunch: false Tags: - Key: Name Value: 'Private Subnet' PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: 'Public Route Table' PublicRoute: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: '0.0.0.0/0' GatewayId: !Ref InternetGateway PublicSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet RouteTableId: !Ref PublicRouteTable EC2InstancePublic: Type: AWS::EC2::Instance Properties: ImageId: 'ami-0c55b159cbfafe1f0' InstanceType: t2.micro NetworkInterfaces: - AssociatePublicIpAddress: "true" DeviceIndex: "0" GroupSet: - !Ref WAFSecurityGroup SubnetId: !Ref PublicSubnet Tags: - Key: Name Value: Amazon EC2 Public EC2InstancePrivate: Type: AWS::EC2::Instance Properties: ImageId: 'ami-0c55b159cbfafe1f0' InstanceType: t2.micro SubnetId: !Ref PrivateSubnet Tags: - Key: Name Value: Amazon EC2 Private WAFSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable HTTP access via port 80 SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 VpcId: !Ref VPC ELB: Type: AWS::ElasticLoadBalancing::LoadBalancer Properties: Listeners: - LoadBalancerPort: 80 InstancePort: 80 Protocol: HTTP AvailabilityZones: - !GetAtt PublicSubnet.AvailabilityZone - !GetAtt PrivateSubnet.AvailabilityZone Instances: - !Ref EC2InstancePublic - !Ref EC2InstancePrivate SecurityGroups: - !GetAtt WAFSecurityGroup.GroupId WAF: Type: AWS::WAFv2::WebACL Properties: Name: SampleWebACL Scope: REGIONAL DefaultAction: Allow: {} VisibilityConfig: SampledRequestsEnabled: true CloudWatchMetricsEnabled: true MetricName: SampleWebACL ElasticLoadBalancingV2Association: Type: AWS::WAFv2::WebACLAssociation Properties: ResourceArn: !Ref ELB WebACLArn: !GetAtt 'WAF.Arn' RDSInstance: Type: AWS::RDS::DBInstance Properties: DBName: 'mydb' Engine: 'MySQL' MasterUsername: 'admin' MasterUserPassword: !Sub '{{resolve:secretsmanager:MyRDSSecret:SecretString:password}}' DBInstanceClass: 'db.t3.micro' AllocatedStorage: '5' VPCSecurityGroups: - !GetAtt WAFSecurityGroup.GroupId DBSubnetGroupName: !Ref RDSSubnetGroup RDSSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: 'Subnet group for RDS' SubnetIds: - !Ref PrivateSubnet - !Ref PublicSubnet CertificateManager: Type: AWS::CertificateManager::Certificate Properties: DomainName: 'example.com' ValidationMethod: DNS
このテンプレートでは、以下のリソースが作成されます。
- VPC
- パブリックサブネットとプライベートサブネット
- インターネットゲートウェイ
- パブリックとプライベートのEC2インスタンス
- Elastic Load Balancing
- AWS WAF
- RDS (パブリックとプライベートサブネットに配置)
- AWS Certificate Manager
一部のパラメータ(例: AMI IDやドメイン名)は、実際の環境に合わせて変更する必要があります。 またRDSのパスワードはAWS Secrets Managerに事前に登録しておく必要があります。
Claude 3 Opus: イベント駆動
以下は、提供された構成図に基づいてCloudFormationテンプレートを生成したものです。
AWSTemplateFormatVersion: '2010-09-09' Description: 'AWS architecture diagram with S3 and Lambda' Resources: OriginalS3Bucket: Type: AWS::S3::Bucket Properties: BucketName: 'original-image-bucket' ThumbnailS3Bucket: Type: AWS::S3::Bucket Properties: BucketName: 'thumbnail-image-bucket' LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - s3:GetObject Resource: !Join ['', ['arn:aws:s3:::', !Ref OriginalS3Bucket, '/*']] - Effect: Allow Action: - s3:PutObject Resource: !Join ['', ['arn:aws:s3:::', !Ref ThumbnailS3Bucket, '/*']] CreateThumbnailLambda: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | const AWS = require('aws-sdk'); const s3 = new AWS.S3(); const sharp = require('sharp'); exports.handler = async (event, context) => { const bucket = event.Records[0].s3.bucket.name; const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); const params = { Bucket: bucket, Key: key }; try { const image = await s3.getObject(params).promise(); const resizedImage = await sharp(image.Body).resize(200).toBuffer(); const newKey = `thumbnail-${key}`; await s3.putObject({ Bucket: '${self:custom.thumbnailBucket}', Key: newKey, Body: resizedImage }).promise(); return { statusCode: 200, body: 'Thumbnail created successfully' }; } catch (error) { console.log(error); return { statusCode: 500, body: error }; } }; Runtime: nodejs12.x Timeout: 10 S3LambdaTrigger: Type: AWS::Lambda::EventSourceMapping Properties: EventSourceArn: !GetAtt OriginalS3Bucket.Arn FunctionName: !GetAtt CreateThumbnailLambda.Arn Events: - s3:ObjectCreated:*
このテンプレートでは、以下のリソースが作成されます。
- オリジナル画像用のS3バケット
- サムネイル画像用のS3バケット
- Lambda関数の実行ロール(S3読み書きとCloudWatch Logsへのアクセス権限を付与)
- サムネイル画像を生成するLambda関数
- オリジナル画像のS3バケットへのPUT操作をトリガーとするLambda関数の設定
Lambda関数のコードは、S3バケットにアップロードされたオリジナル画像を取得し、sharpライブラリを使用して画像のサイズを変更し、サムネイル画像をS3バケットに保存します。
注: このテンプレートを使用する前に、S3バケット名を一意の名前に変更し、Lambda関数に必要なライブラリ(sharpなど)をパッケージ化してデプロイパッケージを作成する必要があります。
また、今回は生成したファイルのうち、Claude 3 Opusで生成したイベント駆動のものをデプロイしてみました。
デプロイ前に構文チェックをしてみると、特にエラーは検出されません。
[cloudshell-user@ip-10-132-33-140 ~]$ aws cloudformation validate-template --template-body file://test06.yaml { "Parameters": [], "Description": "AWS architecture diagram with S3 and Lambda", "Capabilities": [ "CAPABILITY_IAM" ], "CapabilitiesReason": "The following resource(s) require capabilities: [AWS::IAM::Role]" } [cloudshell-user@ip-10-132-33-140 ~]$
ただしこのままCloudFormationをデプロイしようとすると、いくつかのエラーが発生しました。
- S3バケット名が重複する
- Lambda関数で
nodejs 12.x
をサポートしていない AWS::Lambda::EventSourceMapping
でEvents
というPropertyはサポートしていない
完璧に生成できるとは限りませんが、ここで生成したファイルをベースに修正をすれば、CloudFormationの定義ファイルをイチから用意するよりは早く作れそうです。