CloudFormationは、AWSインフラをコードで管理するための強力なサービスです。本記事では、IaC比較記事(IaC比較)で触れなかった高度な機能を詳しく解説します。
カスタムリソース
概要
flowchart TB
subgraph CustomResource["カスタムリソース"]
CFn["CloudFormation"]
Lambda["Lambda関数"]
SNS["SNS"]
end
CFn --> |"Create/Update/Delete"| Lambda
Lambda --> |"応答"| CFn
CFn --> |"Create/Update/Delete"| SNS
SNS --> |"外部システム"| External["外部サービス"]
style CustomResource fill:#3b82f6,color:#fff
Lambda-backed カスタムリソース
Resources:
CustomResourceFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: cfn-custom-resource
Runtime: python3.11
Handler: index.handler
Timeout: 300
Role: !GetAtt CustomResourceRole.Arn
Code:
ZipFile: |
import cfnresponse
import boto3
def handler(event, context):
try:
request_type = event['RequestType']
properties = event['ResourceProperties']
if request_type == 'Create':
result = create_resource(properties)
elif request_type == 'Update':
result = update_resource(properties, event['OldResourceProperties'])
elif request_type == 'Delete':
result = delete_resource(properties)
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Result': result
}, physicalResourceId=result.get('id', 'custom-resource'))
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': str(e)
})
def create_resource(properties):
return {'id': 'resource-123', 'status': 'created'}
def update_resource(properties, old_properties):
return {'id': 'resource-123', 'status': 'updated'}
def delete_resource(properties):
return {'status': 'deleted'}
MyCustomResource:
Type: Custom::MyResource
Properties:
ServiceToken: !GetAtt CustomResourceFunction.Arn
CustomParam1: !Ref Parameter1
CustomParam2: !Ref Parameter2
cfn-responseモジュール
import json
import urllib3
SUCCESS = "SUCCESS"
FAILED = "FAILED"
def send(event, context, response_status, response_data,
physical_resource_id=None, no_echo=False, reason=None):
response_url = event['ResponseURL']
response_body = {
'Status': response_status,
'Reason': reason or f"See CloudWatch Log Stream: {context.log_stream_name}",
'PhysicalResourceId': physical_resource_id or context.log_stream_name,
'StackId': event['StackId'],
'RequestId': event['RequestId'],
'LogicalResourceId': event['LogicalResourceId'],
'NoEcho': no_echo,
'Data': response_data
}
http = urllib3.PoolManager()
http.request(
'PUT',
response_url,
body=json.dumps(response_body).encode('utf-8'),
headers={'Content-Type': ''}
)
ユースケース
| ユースケース |
説明 |
| 外部API呼び出し |
Slack通知、外部サービス連携 |
| 複雑な初期化 |
DB初期データ投入 |
| CloudFormation未サポートリソース |
新サービス対応 |
| クロスアカウント操作 |
別アカウントのリソース管理 |
CloudFormationマクロ
マクロの仕組み
flowchart LR
subgraph Macro["マクロ処理"]
Template["テンプレート"]
Transform["Transform"]
Lambda["Lambda"]
Result["展開後テンプレート"]
end
Template --> Transform
Transform --> Lambda
Lambda --> Result
Result --> Stack["スタック作成"]
style Macro fill:#f59e0b,color:#000
マクロの作成
MacroFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: cfn-macro-add-tags
Runtime: python3.11
Handler: index.handler
Role: !GetAtt MacroRole.Arn
Code:
ZipFile: |
def handler(event, context):
fragment = event['fragment']
params = event['params']
template_params = event['templateParameterValues']
default_tags = params.get('DefaultTags', [])
for resource_name, resource in fragment.get('Resources', {}).items():
if 'Properties' not in resource:
resource['Properties'] = {}
existing_tags = resource['Properties'].get('Tags', [])
resource['Properties']['Tags'] = existing_tags + default_tags
return {
'requestId': event['requestId'],
'status': 'success',
'fragment': fragment
}
MacroDefinition:
Type: AWS::CloudFormation::Macro
Properties:
Name: AddDefaultTags
FunctionName: !GetAtt MacroFunction.Arn
マクロの使用
AWSTemplateFormatVersion: '2010-09-09'
Transform:
- Name: AddDefaultTags
Parameters:
DefaultTags:
- Key: Environment
Value: production
- Key: ManagedBy
Value: CloudFormation
Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-bucket
MyQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: my-queue
AWS::Include変換
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Include
Resources:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: s3://my-bucket/templates/common-resources.yaml
StackSets詳細
アーキテクチャ
flowchart TB
subgraph Management["管理アカウント"]
StackSet["StackSet"]
end
subgraph OU["組織単位"]
Account1["アカウント1"]
Account2["アカウント2"]
Account3["アカウント3"]
end
subgraph Regions["リージョン"]
Region1["ap-northeast-1"]
Region2["us-east-1"]
end
StackSet --> Account1
StackSet --> Account2
StackSet --> Account3
Account1 --> Region1
Account1 --> Region2
style Management fill:#3b82f6,color:#fff
style OU fill:#22c55e,color:#fff
権限モデル
| モデル |
説明 |
用途 |
| SELF_MANAGED |
手動でIAMロール作成 |
特定アカウントへの展開 |
| SERVICE_MANAGED |
Organizations統合 |
OU全体への自動展開 |
SERVICE_MANAGED設定
StackSet:
Type: AWS::CloudFormation::StackSet
Properties:
StackSetName: security-baseline
Description: Security baseline for all accounts
PermissionModel: SERVICE_MANAGED
AutoDeployment:
Enabled: true
RetainStacksOnAccountRemoval: false
StackInstancesGroup:
- DeploymentTargets:
OrganizationalUnitIds:
- ou-xxxx-yyyyyyyy
Regions:
- ap-northeast-1
- us-east-1
OperationPreferences:
FailureTolerancePercentage: 10
MaxConcurrentPercentage: 25
RegionConcurrencyType: PARALLEL
CallAs: DELEGATED_ADMIN
TemplateBody: |
AWSTemplateFormatVersion: '2010-09-09'
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Default security group
デプロイオプション
OperationPreferences:
FailureToleranceCount: 2
FailureTolerancePercentage: 10
MaxConcurrentCount: 5
MaxConcurrentPercentage: 25
RegionConcurrencyType: PARALLEL
RegionOrder:
- ap-northeast-1
- us-east-1
- eu-west-1
ドリフト検出
ドリフト検出フロー
flowchart TB
subgraph DriftDetection["ドリフト検出"]
Detect["検出開始"]
Compare["比較"]
Result["結果"]
end
subgraph Status["ステータス"]
InSync["IN_SYNC"]
Drifted["DRIFTED"]
NotChecked["NOT_CHECKED"]
end
Detect --> Compare
Compare --> Result
Result --> Status
style Drifted fill:#ef4444,color:#fff
style InSync fill:#22c55e,color:#fff
ドリフト検出の実行
aws cloudformation detect-stack-drift \
--stack-name my-stack
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id xxxxx
aws cloudformation describe-stack-resource-drifts \
--stack-name my-stack \
--stack-resource-drift-status-filters MODIFIED DELETED
自動ドリフト検出
DriftDetectionRule:
Type: AWS::Events::Rule
Properties:
ScheduleExpression: rate(1 day)
Targets:
- Id: DetectDrift
Arn: !GetAtt DriftDetectionLambda.Arn
DriftDetectionLambda:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
cfn = boto3.client('cloudformation')
sns = boto3.client('sns')
def handler(event, context):
stacks = cfn.list_stacks(
StackStatusFilter=['CREATE_COMPLETE', 'UPDATE_COMPLETE']
)
for stack in stacks['StackSummaries']:
detection_id = cfn.detect_stack_drift(
StackName=stack['StackName']
)['StackDriftDetectionId']
変更セット
変更セットのフロー
flowchart LR
subgraph ChangeSet["変更セット"]
Create["作成"]
Review["レビュー"]
Execute["実行"]
end
Create --> Review
Review --> |"承認"| Execute
Review --> |"却下"| Delete["削除"]
style ChangeSet fill:#3b82f6,color:#fff
変更セットの操作
aws cloudformation create-change-set \
--stack-name my-stack \
--change-set-name my-changes \
--template-body file://template.yaml \
--parameters ParameterKey=Env,ParameterValue=prod \
--change-set-type UPDATE
aws cloudformation describe-change-set \
--stack-name my-stack \
--change-set-name my-changes
aws cloudformation execute-change-set \
--stack-name my-stack \
--change-set-name my-changes
aws cloudformation delete-change-set \
--stack-name my-stack \
--change-set-name my-changes
変更タイプ
| タイプ |
説明 |
| Add |
新規リソース追加 |
| Modify |
既存リソース変更 |
| Remove |
リソース削除 |
| Dynamic |
実行時まで不明 |
リソースインポート
インポートフロー
flowchart TB
subgraph Import["リソースインポート"]
Existing["既存リソース"]
Template["テンプレート定義"]
ChangeSet["インポート変更セット"]
Stack["スタック"]
end
Existing --> Template
Template --> ChangeSet
ChangeSet --> Stack
style Import fill:#22c55e,color:#fff
インポート手順
Resources:
ImportedBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: existing-bucket-name
aws cloudformation create-change-set \
--stack-name my-stack \
--change-set-name import-bucket \
--change-set-type IMPORT \
--resources-to-import "[{\"ResourceType\":\"AWS::S3::Bucket\",\"LogicalResourceId\":\"ImportedBucket\",\"ResourceIdentifier\":{\"BucketName\":\"existing-bucket-name\"}}]" \
--template-body file://template.yaml
aws cloudformation execute-change-set \
--stack-name my-stack \
--change-set-name import-bucket
スタックポリシー
ポリシー例
{
"Statement": [
{
"Effect": "Allow",
"Action": "Update:*",
"Principal": "*",
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"Update:Replace",
"Update:Delete"
],
"Principal": "*",
"Resource": "LogicalResourceId/ProductionDatabase",
"Condition": {
"StringEquals": {
"ResourceType": ["AWS::RDS::DBInstance"]
}
}
}
]
}
ポリシーの適用
aws cloudformation set-stack-policy \
--stack-name my-stack \
--stack-policy-body file://stack-policy.json
aws cloudformation update-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--stack-policy-during-update-body file://override-policy.json
高度なテンプレート技法
条件付きリソース
Conditions:
IsProduction: !Equals [!Ref Environment, production]
HasBackup: !And
- !Condition IsProduction
- !Equals [!Ref EnableBackup, "true"]
Resources:
ProductionOnlyResource:
Type: AWS::RDS::DBInstance
Condition: IsProduction
Properties:
BackupResource:
Type: AWS::Backup::BackupPlan
Condition: HasBackup
Properties:
動的参照
Resources:
MyInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}'
KeyName: '{{resolve:ssm:/myapp/ec2/keypair}}'
Tags:
- Key: Password
Value: '{{resolve:secretsmanager:MySecret:SecretString:password}}'
ネストスタック
Resources:
VPCStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/vpc.yaml
Parameters:
Environment: !Ref Environment
TimeoutInMinutes: 20
AppStack:
Type: AWS::CloudFormation::Stack
DependsOn: VPCStack
Properties:
TemplateURL: https://s3.amazonaws.com/my-bucket/app.yaml
Parameters:
VPCId: !GetAtt VPCStack.Outputs.VPCId
SubnetIds: !GetAtt VPCStack.Outputs.PrivateSubnetIds
ベストプラクティス
flowchart TB
subgraph BestPractices["Best Practices"]
Modular["モジュール化"]
DeletionPolicy["削除保護"]
ChangeSet["変更セット活用"]
Drift["ドリフト監視"]
end
style BestPractices fill:#22c55e,color:#fff
| カテゴリ |
項目 |
| 設計 |
ネストスタックで再利用 |
| 安全性 |
DeletionPolicy設定 |
| 運用 |
変更セットで事前確認 |
| 監視 |
定期的なドリフト検出 |
まとめ
| 機能 |
用途 |
| カスタムリソース |
CloudFormation未対応処理 |
| マクロ |
テンプレート変換 |
| StackSets |
マルチアカウント展開 |
| ドリフト検出 |
設定変更の検知 |
| 変更セット |
安全な更新 |
CloudFormationの高度な機能を活用することで、複雑なインフラ要件にも対応できます。
参考資料