
AWS CloudFormationで循環依存エラーを解決する書き方を整理してみた
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS CloudFormationで循環依存エラーを解決したい
おのやんです。
みなさん、AWS CloudFormation (以下、CFn) で循環依存エラーに遭遇したことはありませんか?私は何度かあります。
セキュリティグループなどのリソースでは、CFnで記述する際に自らを参照するケースが多々あります。この際、CFnの仕様によってリソースの作成が失敗する場合があります。この解消方法についていくつか試行錯誤してみたので、今回はそちらを紹介したいと思います。
循環依存によるエラーとは?
循環依存とは、1つのリソースがそれ自体に依存していること、あるいは2つのリソースが互いに依存していることを指します。
まず例として、リソースAとリソースBがお互いに依存しているケースを考えます。

テンプレートで複数のリソースを定義すると、CFnはそれらのリソースを並列に作成しようとします。ここで、リソース内のDependsOn属性を利用することで、リソースの作成順序を制御できます。DependsOnを使用すると、リソースBの前にリソースAを作成することが可能です。また組み込み関数Refなどを使用して別のリソースを参照する場合にも、暗黙の依存関係が生まれます。リソースAのプロパティにリソースBへのRefがある場合、リソースBはリソースAより先に作成されます。
この依存関係が2つのリソース間でお互いに存在している場合、どちらのリソースを最初に作成すべきかCFnが判断できなくなります。これにより、循環依存エラーが発生するというわけです。

1つのリソースがそれ自体に依存しているケース
それでは、実際に循環依存エラーが出るケースと、それに対する対処法をまとめていきたと思います。
こちらの画像のように1つのリソースの設定にそれ自身をしていている場合、CFnでは循環依存エラーになります。

CFnテンプレートの例がこちらです。あるセキュリティグループのインバウンドルールで、セキュリティグループそれ自体を許可しています。
Resources:
SecurityGroupA: # Circular Dependencies!
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: security-group-a
GroupDescription: security-group-a
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !GetAtt SecurityGroupA.GroupId # Circular Dependencies!
CFnの仕様を考えると、インバウンドルールが適用されるSecurityGroupAよりも、インバウンドルールで許可する接続元のリソースSecurityGroupAが先に作成されるはずです。しかしSecurityGroupAが作成されようとしても、SecurityGroupAが作成されていないのでSecurityGroupAは作成されません (以下無限ループ) ...
このように、1つのリソースがそれ自体に依存している場合は、循環依存が発生して永遠にリソースが作成されません。こちらのようにCircular Dependencies for resource SecurityGroupAエラーが出て作成に失敗します。
E3004: Circular Dependencies for resource SecurityGroupA. Circular dependency with [SecurityGroupA]
こちらを解決する書き方がこちらです。AWS::EC2::SecurityGroupIngressを別途追加し、そこにルールを記述しています。
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: security-group-a
GroupDescription: security-group-a
SecurityGroupAIngress: # インバウンドルールを外に出す
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupA
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !GetAtt SecurityGroupA.GroupId
この書き方で記述すれば、SecurityGroupAが作成された後に、SecurityGroupAに依存しているSecurityGroupAIngressが作成されます。循環依存も発生せずに、正常にリソースが作成されます。
2つのリソースが互いに依存しているケース
こちらの画像のように2つのリソースの設定にお互いを指定している場合、CFnでは循環依存としてエラーになります。

CFnテンプレートの例がこちらです。SecurityGroupAのルールにてSecurityGroupBからのアクセスを許可しています。同時に、SecurityGroupBのルールにてSecurityGroupAからのアクセスを許可しています
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: security-group-a
GroupDescription: security-group-a
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !GetAtt SecurityGroupB.GroupId # Circular Dependencies!
SecurityGroupB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: security-group-b
GroupDescription: security-group-b
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !GetAtt SecurityGroupA.GroupId # Circular Dependencies!
SecurityGroupA・Bの間で双方向の暗黙的な依存関係が生まれています。そのため、どちらを先に作成するべきかCFnが判断できなくなります。結果、Circular Dependencies for resource SecurityGroupXの循環依存エラーとなります。
E3004: Circular Dependencies for resource SecurityGroupA. Circular dependency with [SecurityGroupB] E3004: Circular Dependencies for resource SecurityGroupB. Circular dependency with [SecurityGroupA]
こちらも同様の書き方で修正できます。AWS::EC2::SecurityGroupIngressを外に出して記述することで、循環依存を断ち切っています。
Resources:
SecurityGroupA:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: security-group-a
GroupDescription: security-group-a
SecurityGroupB:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: security-group-b
GroupDescription: security-group-b
SecurityGroupAIngress: # インバウンドルールを外に出す
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupA
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !GetAtt SecurityGroupB.GroupId
SecurityGroupBIngress: # インバウンドルールを外に出す
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroupB
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !GetAtt SecurityGroupA.GroupId
この場合、何にも依存していないSecurityGroupBが先に作成されます。その後に、SecurityGroupBに依存しているSecurityGroupAが作成されます。
循環依存エラーは、依存関係を断ち切ることで解決できる
セキュリティグループを作っているときあるあるのネタですが、意外と情報が少なかったのでまとめてみました。
AWSの公式ブログにも循環依存エラーについて紹介されていますので、参考になれば幸いです。
では!






