やりたいこと
- CloudFormationでStackを1つ作る。
- 別のStackを作る。このStackの中で[1]で作成したResourceを変更する。
- [1]で作ったスタックをアップデートする。とどうなる?
- [2]の変更で[1]で作成したリソースが変更できないようにする。
※[4]は、今の所、できていない。もう少し模索する(ToDo)
1. CloudFormationでStackを1つ作る
AWSTemplateFormatVersion: "2010-09-09"
Description:
VPC, Subnet
Metadata:
"AWS::CloudFormation::Interface":
ParameterGroups:
- Label:
default: "Project Name Prefix"
Parameters:
- ClusterPrefix
- Label:
default: "Network Configuration"
Parameters:
- VPCCIDR
- PublicSubnetCIDR
ParameterLabels:
VPCCIDR:
default: "VPC CIDR"
PublicSubnetCIDR:
default: "PublicSubnet CIDR"
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
ClusterPrefix:
Type: String
Default: "foobar"
VPCCIDR:
Type: String
Default: "10.0.0.0/16"
PublicSubnetCIDR:
Type: String
Default: "10.0.10.0/24"
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
# VPC
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: "true"
EnableDnsHostnames: "true"
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub "${ClusterPrefix}-vpc"
# InternetGateway
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: Name
Value: !Sub "${ClusterPrefix}-igw"
# IGW Attach
InternetGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# DHCPOptions
DHCPOptions:
Type: AWS::EC2::DHCPOptions
Properties:
DomainName: ap-northeast-1.compute.internal
DomainNameServers:
- AmazonProvidedDNS
Tags:
- Key: Name
Value: !Sub "${ClusterPrefix}-dhcp-options"
DHCPOptionsAssociation:
Type: AWS::EC2::VPCDHCPOptionsAssociation
Properties:
VpcId: !Ref VPC
DhcpOptionsId: !Ref DHCPOptions
# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
# PublicSubnet
PublicSubnet:
Type: "AWS::EC2::Subnet"
Properties:
AvailabilityZone: "ap-northeast-1a"
CidrBlock: !Ref PublicSubnetCIDR
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ClusterPrefix}-public-subnet-a"
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
# PublicRouteTable
PublicRouteTable:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ClusterPrefix}-public-route-a"
# ------------------------------------------------------------#
# Route
# ------------------------------------------------------------#
# PublicRouteA
PublicRouteA:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------#
# PublicSubnetRouteTableAssociation
PublicSubnetRouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
# Outputs
Outputs:
VPC:
Value: !Ref VPC
PublicSubnet:
Value: !Ref PublicSubnet
InternetGateway:
Value: !Ref InternetGateway
OutputsでRouteTableとIGWとVPCのIDを出力しているので、コピーしておく。
別のStackを作る。このStackの中で[1]で作成したResourceを変更する。
[1]で作ったRouteTableをすげ替えるようなRouteの設定とは違う設定を突っ込む。
AWSTemplateFormatVersion: "2010-09-09"
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
ClusterPrefix:
Type: String
Default: "foobar"
VPC:
Type: String
Default: ""
InternetGateway:
Type: String
Default: ""
PublicSubnet:
Type: String
Default: ""
Resources:
# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
# PublicRouteTableMod
PublicRouteTableMod:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${ClusterPrefix}-public-route-a-mod"
# ------------------------------------------------------------#
# Route
# ------------------------------------------------------------#
# PublicRouteMod
PublicRouteMod:
Type: "AWS::EC2::Route"
Properties:
RouteTableId: !Ref PublicRouteTableMod
DestinationCidrBlock: "192.168.0.0/24"
GatewayId: !Ref InternetGateway
# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------#
# PublicSubnetARouteTableAssociationMod
PublicSubnetARouteTableAssociationMod:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTableMod
RouteTableを確認するとあとから作ったやつが、Subnetに紐付けされており、
紐付けされているRouteもあとから作ったやつになっている。
3. [1]で作ったスタックをアップデートする。とどうなる?
先ずは、何も考えずに[1]のStackを更新しちゃう。
ここではPrivateSubnetを作る感じ。
36a37,39
> PrivateSubnetCIDR:
> Type: String
> Default: "10.0.20.0/24"
97a101,110
> # PrivateSubnet
> PrivateSubnet:
> Type: "AWS::EC2::Subnet"
> Properties:
> AvailabilityZone: "ap-northeast-1a"
> CidrBlock: !Ref PrivateSubnetCIDR
> VpcId: !Ref VPC
> Tags:
> - Key: Name
> Value: !Sub "${ClusterPrefix}-private-subnet-a"
StackのEventを見ると以下の通り。ん~、YAMLの差分更新しかしない感じなのね。
では、今度はPublicSubnetのRouteTableをアップデートしてみる。
132c132
< DestinationCidrBlock: "0.0.0.0/0"
---
> DestinationCidrBlock: "172.16.0.0/12"
この更新の場合の変更セットは以下のようになる。
むむむ、RouteTableが削除&再作成されている。
結果、RouteTableは再作成されて、意図したルーティング設定にはなっているものの、肝心のRouteTableがSubnetにアタッチされていないという、グッチャグチャの構成に...。stack01で作ったRouteTableがSubnetにアタッチされるのかと思いきや、やはりYAMLの差分のみの適用を行う動きをするようで、Associationし直すということはなかった。
4. [2]の変更で[1]で作成したリソースが変更できないようにする。
[2]の変更で[1]の構成を崩したが、これを阻止したいときに使うのが、stack policyらしい。
スタックを作成するとき、すべてのリソースですべての更新アクションが許可されます。デフォルトでは、スタック更新アクセス権限を持つユーザーであれば誰でもスタック内のすべてのリソースを更新できます。更新処理中、リソースの中断や完全な置き換えが必要になった場合は、新しい物理 ID やまったく新しいストレージが使用される結果になります。スタックポリシーを使用すると、スタックの更新中にスタックのリソースが意図せずに更新または削除されるのを防止できます。スタックポリシーは、指定したリソースに対して実行できる更新アクションを定義する JSON ドキュメントです。
スタックポリシーを設定すると、デフォルトでスタック内のすべてのリソースが保護されます。特定のリソースでの更新を許可するには、スタックポリシーでこれらのリソースに対して Allow ステートメントを明示的に指定します。1 つのスタックに定義できるスタックポリシーは 1 つのみですが、1 つのポリシー内で複数のリソースを保護することができます。スタックポリシーは、スタックを更新しようとするすべての AWS CloudFormation ユーザーに適用されます。異なるスタックポリシーを異なるユーザーに関連付けることはできません。
スタックポリシーは、スタックの更新時のみ適用されます。AWS Identity and Access Management (IAM) ポリシーのようなアクセスコントロールは提供しません。特定のスタックリソースを誤って更新しないように、スタックポリシーはフェイルセーフメカニズムとしてのみ使用してください。AWS リソースまたはアクションへのアクセスを制御するには、IAM を使用します。
[1]の実施時に、stack policyを設定する。
[2]では、新たにRouteTableを作ってアタッチするようなYAMLなので、これをブロックするポリシーにする。
{
"Statement" : [
{
"Effect" : "Allow",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "*"
},
{
"Effect" : "Deny",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "LogicalResourceId/PublicSubnetRouteTableAssociation"
}
]
}
再度、[2]を実施すると...
失敗させたかったけど、同一スタックの更新にのみ適用できるみたい?なのかな。普通に変更できてしまった。