はじめに
CloudFormationを使ってCI/CDパイプラインを作成する機会がありましたが、正直なところコードを書くのが少し面倒でした。
そこで、「どうせなら効率よく作成したい!」という思いから、Infrastructure Composerを試してみることにしました。
本記事では、その動機や背景、実際に試してみた内容を紹介します。
Infrastructure Composerとは
Infrastructure Composerは、AWSが提供するIaCツールであるCloudFormationの一機能です。
コードを書く代わりに、GUI上でリソースを視覚的に設計し、その結果をCloudFormationテンプレートとしてエクスポートできる便利なツールです。
特徴
- コード不要: コードを書くのが苦手な方でもGUI操作だけでテンプレートを作成可能。
- 視覚的な設計: リソース間の依存関係や全体構成を視覚的に確認しながら作業が進められる。
- エクスポート機能: 完成した構成をそのままCloudFormationテンプレート(YAML/JSON形式)としてダウンロード可能。
こんな人におすすめ
- コードよりも直感的な操作を重視したい人。
- AWSリソースの設計を構成図としてイメージしながら進めたい人。
- CloudFormationに慣れていない初心者。
注意点
- 対応リソースに制限あり: 一部のAWSリソースはまだInfrastructure Composerでサポートされていません。
- 複雑な構成には非推奨: GUIでの操作が煩雑になる場合や、高度なカスタマイズが必要な場合は、手動でテンプレートを作成した方が効率的です。
Infrastructure Composerを活用することで、複雑なコードを書くことなく、シンプルなAWS環境をすばやく構築することが可能です。本記事では、このツールを使った実際の操作手順や注意点について解説します。
前提条件
今回はTerraformをデプロイするためのCI/CD基盤を構築します。
本題から逸れるため、あまり深くは記載しませんが、
TerraformをAWS環境上で実行する際には、状態管理ファイルであるtfstateを保管するS3バケットと、それの書き込みを管理するDynamoDBを配置するのがベストプラクティスです。
この背景を踏まえつつ、Infrastructure Composerを活用して効率的にテンプレートを作成する方法をお伝えします。
やってみる
事前準備
それでは今回必要なリソースの一覧を列挙します。
- Codepipeline
- CodeBuild(plan/静的テスト用)
- CodeBuild(apply用)
- S3(アーティファクト用)
- S3(tfstate保管用)
- S3(Terraformコード格納用)
- DynamoDB(tfstateロック用)
この構成図を元にInfrastructure Conposerにリソースを配置していきます。
リソースの配置
まずリソースを配置していきます。リソースの配置はサイドバーからドラック&ドロップでできます。
これを必要なリソース分繰り返していきます。
最終的には以下のような形になりました。
リソースのパラメータを入力
では各リソースのパラメータを入力していきます。
パラメータはリソースをクリックすると、右側にサイドバーが出現し、設定できるようになります。
ここからはCloudFormaionのドキュメントを参照し、パラメータを入力していきます。
またほとんどのリソースはIaCとして記載する必要がありますが、Enhanced componentsと呼ばれるコンポーネントは、パラメータを入力するだけで自動でIaCコードが生成されるみたいです。
現時点でEnhanced componentsは以下のリソースをサポートしています。
- APIGateway
- Cognito UserPool
- Cognito UserPoolClient
- DynamoDB Table
- EventBridge EventRole
- Kinesis Stream
- Lambda Function
- Lambda Layer
- RDS Database
- S3 Bucket
- SNS Topic
- SQS Queue
- StepFunctions State machine
主にサーバレス系のサービスが網羅されているような印象を受けました。
CloudFormationコードの出力
CloudFormationコードは左上のTempleteから確認できます。
作成したテンプレートは以下のようになります。
テンプレート
Resources:
Project:
Type: AWS::CodeBuild::Project
Properties:
Name: myProjectName
Description: A description about my project
ServiceRole: !GetAtt Role.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:1.0
Source:
Type: CODEPIPELINE
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: AWS
Version: '1'
Provider: S3
OutputArtifacts:
- Name: SourceOutput
Configuration:
S3Bucket: !GetAtt artifactFileBucket.bucket
S3ObjectKey: SourceArtifact
RunOrder: 1
- Name: Plan
Actions:
- Name: PlanAction
InputArtifacts:
- Name: SourceOutput
ActionTypeId:
Category: Build
Owner: AWS
Version: '1'
Provider: CodeBuild
RunOrder: 1
OutputArtifacts:
- Name: PlanOutput
- Name: Apply
Actions:
- Name: ApplyAction
InputArtifacts:
- Name: PlanOutput
ActionTypeId:
Category: Build
Owner: AWS
Version: '1'
Provider: CodeBuild
RunOrder: 1
RoleArn: !GetAtt Role3.Arn
Project2:
Type: AWS::CodeBuild::Project
Properties:
Name: myProjectName
Description: A description about my project
ServiceRole: !GetAtt Role2.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/standard:1.0
Source:
Type: CODEPIPELINE
Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- sts:AssumeRole
Policy:
Type: AWS::IAM::Policy
Properties:
PolicyName: build-plan-policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- '*'
Resource: '*'
Role2:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Action:
- sts:AssumeRole
Role3:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Policy2:
Type: AWS::IAM::Policy
Properties:
PolicyName: build-apply-policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- '*'
Resource: '*'
Policy3:
Type: AWS::IAM::Policy
Properties:
PolicyName: pipeline-policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- '*'
Resource: '*'
stateRockDynamoDB:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: RockID
AttributeType: S
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: RockID
KeyType: HASH
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
stateFileBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-stateFileBucket-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: alias/aws/s3
PublicAccessBlockConfiguration:
IgnorePublicAcls: true
RestrictPublicBuckets: true
stateFileBucketBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref stateFileBucket
PolicyDocument:
Id: RequireEncryptionInTransit
Version: '2012-10-17'
Statement:
- Principal: '*'
Action: '*'
Effect: Deny
Resource:
- !GetAtt stateFileBucket.Arn
- !Sub ${stateFileBucket.Arn}/*
Condition:
Bool:
aws:SecureTransport: 'false'
codeFileBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-codeFileBucket-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: alias/aws/s3
PublicAccessBlockConfiguration:
IgnorePublicAcls: true
RestrictPublicBuckets: true
codeFileBucketBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref codeFileBucket
PolicyDocument:
Id: RequireEncryptionInTransit
Version: '2012-10-17'
Statement:
- Principal: '*'
Action: '*'
Effect: Deny
Resource:
- !GetAtt codeFileBucket.Arn
- !Sub ${codeFileBucket.Arn}/*
Condition:
Bool:
aws:SecureTransport: 'false'
artifactFileBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-artifactFileBucket-${AWS::AccountId}
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: alias/aws/s3
PublicAccessBlockConfiguration:
IgnorePublicAcls: true
RestrictPublicBuckets: true
artifactFileBucketBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref artifactFileBucket
PolicyDocument:
Id: RequireEncryptionInTransit
Version: '2012-10-17'
Statement:
- Principal: '*'
Action: '*'
Effect: Deny
Resource:
- !GetAtt artifactFileBucket.Arn
- !Sub ${artifactFileBucket.Arn}/*
Condition:
Bool:
aws:SecureTransport: 'false'
Transform: AWS::Serverless-2016-10-31
感想
正直まだ使いこなせているわけではないですが、良かった点と微妙な点を挙げてみます。
良かった点
- 構成図からCloudFormationコードを作成できる開発者体験
- 開発環境構築なしで、CloudFormationのLinterのような機能が使える
- CloudFormationコードから構成図への展開が出来る
- かなり多くのリソースをサポートしている
微妙だった点
- ほとんどのリソースは結局CloudFormationコードを書かなければならない
- 自動で整列したときに、縦並びになってしまい見づらい
私としては、ChatGPT等の生成AIを活用しながらコードを書いていく方が、より早く開発できると感じました。
また、CICDを構築するというケースだとうまく活用できませんでした。EnhancedComponentsで定義されているサーバレス系のリソースの場合、上手く活用できる可能性がありそうです。
まとめ
GUIからCloudFormationコードを出力できるInfrastracture Composerを使ってみました。
面白い開発者体験だと感じましたが、結局CloudFormationコードを書かなければならないというところは変わっていないため、活用が難しそうです。
ぜひ皆さんも活用してみてください。