<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CloudFormation | 伊東屋TECHブログ</title>
	<atom:link href="https://tech-itoya.com/tag/cloudformation/feed/" rel="self" type="application/rss+xml" />
	<link>https://tech-itoya.com</link>
	<description>千葉県・船橋市の総合園芸店が運営する技術ブログ</description>
	<lastBuildDate>Sun, 13 Oct 2024 07:54:10 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://tech-itoya.com/wp-content/uploads/2024/10/cropped-itoya-logo-32x32.png</url>
	<title>CloudFormation | 伊東屋TECHブログ</title>
	<link>https://tech-itoya.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>CloudFormationスタックから外れたサブネットをスタック管理下に引き戻したい</title>
		<link>https://tech-itoya.com/cloudformation%e3%82%b9%e3%82%bf%e3%83%83%e3%82%af%e3%81%8b%e3%82%89%e5%a4%96%e3%82%8c%e3%81%9f%e3%82%b5%e3%83%96%e3%83%8d%e3%83%83%e3%83%88%e3%82%92%e3%82%b9%e3%82%bf%e3%83%83%e3%82%af%e7%ae%a1/</link>
					<comments>https://tech-itoya.com/cloudformation%e3%82%b9%e3%82%bf%e3%83%83%e3%82%af%e3%81%8b%e3%82%89%e5%a4%96%e3%82%8c%e3%81%9f%e3%82%b5%e3%83%96%e3%83%8d%e3%83%83%e3%83%88%e3%82%92%e3%82%b9%e3%82%bf%e3%83%83%e3%82%af%e7%ae%a1/#respond</comments>
		
		<dc:creator><![CDATA[アーサー・C・ダントー]]></dc:creator>
		<pubDate>Sun, 13 Oct 2024 07:54:09 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CloudFormation]]></category>
		<category><![CDATA[IaC]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=134</guid>

					<description><![CDATA[こんにちは。ダントーです。 CloudFormationスタックで管理していたサブネットが、スタックの更新・削除により意図せずスタック管理下から外れてしまって焦っている方向けの記事です。 他リソースが依存しているサブネッ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんにちは。ダントーです。</p>



<p>CloudFormationスタックで管理していたサブネットが、スタックの更新・削除により意図せずスタック管理下から外れてしまって焦っている方向けの記事です。</p>



<p>他リソースが依存しているサブネットをスタックから削除しようとすると、<strong>スタックから消えないだけでなく<span class="marker-under">スタック管理下から外れる</span></strong>といった現象に出くわしたので、その際の対応を備忘録として残します。</p>



<p></p>



<p><strong>想定検索ワード</strong>：CloudFormation  スタック  サブネット  外れる</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">TL; DR;</a></li><li><a href="#toc2" tabindex="0">構成図とCloudFormationテンプレート</a></li><li><a href="#toc3" tabindex="0">本題</a><ol><li><a href="#toc4" tabindex="0">状況を再現(スタックを作成する)</a></li><li><a href="#toc5" tabindex="0">状況を再現(スタック管理下からサブネットを外す)</a></li><li><a href="#toc6" tabindex="0">本題：サブネットをスタック管理下に引き戻す</a><ol><li><a href="#toc7" tabindex="0">1. インポート時の設定ファイルを作成する</a></li><li><a href="#toc8" tabindex="0">2. インポート用のCloudFormationテンプレートを作成する</a></li><li><a href="#toc9" tabindex="0">3. インポート実行</a></li><li><a href="#toc10" tabindex="0">4. 削除してしまったその他のリソースを再作成する</a></li><li><a href="#toc11" tabindex="0">5. 復旧を確認</a></li></ol></li></ol></li><li><a href="#toc12" tabindex="0">おまけ + 参考文献</a><ol><li><a href="#toc13" tabindex="0">おまけ</a></li><li><a href="#toc14" tabindex="0">参考文献</a></li></ol></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">TL; DR;</span></h2>



<ol class="wp-block-list">
<li>CloudFormationの既存スタックへのリソースインポート機能を使う
<ul class="wp-block-list">
<li>スタック管理下から外れたサブネットを既存スタックにインポートします</li>
</ul>
</li>



<li>本来デプロイするはずのテンプレートを使用して既存スタックを再度更新します
<ul class="wp-block-list">
<li>消失したリソースがある場合はこの手順も踏んだほうが良いです</li>
</ul>
</li>
</ol>



<p></p>



<p>では、いきましょう。</p>



<h2 class="wp-block-heading"><span id="toc2">構成図とCloudFormationテンプレート</span></h2>



<p>今回想定している構成です。</p>



<p>ポイントはサブネットの存在に依存するリソースが存在していること、つまりサブネット上にVPC Lambdaがデプロイされている点になります。</p>



<p>また、ネットワーク復旧確認用にVPC EndpointとS3を用意しています。</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="943" height="471" src="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-1.png" alt="" class="wp-image-193" srcset="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-1.png 943w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-1-300x150.png 300w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-1-768x384.png 768w" sizes="(max-width: 943px) 100vw, 943px" /></figure>



<h2 class="wp-block-heading"><span id="toc3">本題</span></h2>



<p>まずは、今回使用するCloudFormationテンプレートを確認します。</p>



<p>テンプレートはNetwork.yamlとLambda.yamlの2種類です。</p>



<p>Network.yamlでは</p>



<ul class="wp-block-list">
<li>VPC</li>



<li>ルートテーブル</li>



<li>サブネット</li>



<li>VPCエンドポイント(Gateway)</li>



<li>パラメータストア(VPCID)　　　　　　← Lambda.yamlにパラメータを渡すために作成</li>



<li>パラメータストア(サブネットID)　　　← Lambda.yamlにパラメータを渡すために作成</li>
</ul>



<p>これらを作成しています。</p>



<p>また、Lambda.yamlでは、</p>



<ul class="wp-block-list">
<li>IAMロール</li>



<li>セキュリティグループ</li>



<li>Lambda関数</li>



<li>S3バケット　　　　　　　　　　　　　←ネットワーク疎通確認用</li>
</ul>



<p>これらを作成しています。</p>



<p>ネットワークの疎通が取れているかを確認するために、Lambda関数内ではS3バケットにファイルをはき出すロジックを入れています。</p>



<p>①正常時、②サブネットをスタックに引き戻したとき</p>



<p>これら2つのケースでネットワークに問題がないことを確認します。</p>



<p>詳細は以下のテンプレートを確認してください。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="Network.yaml" data-show-lang="0"><code>AWSTemplateFormatVersion: &quot;2010-09-09&quot;
Description: Network

Parameters:
  Prefix:
    Description: Enter Prefix.
    Type: String
    Default: sample

Resources:
#---------------------------------------------------#
# VPC
#---------------------------------------------------#
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-vpc

#---------------------------------------------------#
# RouteTable
#---------------------------------------------------#
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-private-rt

#---------------------------------------------------#
# Subnet1A and RouteTableAssociation
#---------------------------------------------------#
  PrivateSubnet1A:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.16.0/20
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref AWS::Region
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-private-subnet-1a

  PrivateSubnet1ARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet1A

#---------------------------------------------------#
# S3 VPCEndpoint
#---------------------------------------------------#
  RecoveryS3VPCEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
      VpcEndpointType: Gateway
      VpcId: !Ref VPC
      RouteTableIds:
        - !Ref PrivateRouteTable

#---------------------------------------------------#
# Parameters Section
#---------------------------------------------------#
  ParamRecoveryVPCId:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /Network/VPCId
      Type: String
      Value: !Ref VPC

  ParamPrivateSubnet1AId:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /Network/PrivateSubnet1AId
      Type: String
      Value: !Ref PrivateSubnet1A</code></pre></div>



<p></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="Lambda.yaml"><code>AWSTemplateFormatVersion: &quot;2010-09-09&quot;
Description: Lambda

Parameters:
  Prefix:
    Description: Enter Prefix.
    Type: String
    Default: sample

  ParamVPCId:
    Type: AWS::SSM::Parameter::Value&lt;String&gt;
    Default: /Network/VPCId

  ParamPrivate1ASubnetId:
    Type: AWS::SSM::Parameter::Value&lt;String&gt;
    Default: /Network/PrivateSubnet1AId

Resources:
#---------------------------------------------------#
# IAM Role for Lambda Function
#---------------------------------------------------#
  LambdaFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${Prefix}-lambda-role
      AssumeRolePolicyDocument:
        Version: &quot;2012-10-17&quot;
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - &#39;sts:AssumeRole&#39;
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
        - arn:aws:iam::aws:policy/AmazonS3FullAccess

#---------------------------------------------------#
# SecurityGroup for Lambda Function
#---------------------------------------------------#
  LambdaSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for lambda
      GroupName: !Sub ${Prefix}-lambda-sg
      VpcId: !Ref ParamVPCId

  LambdaSGEgressToANY:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
         IpProtocol: -1
         CidrIp: 0.0.0.0/0
         GroupId: !Ref LambdaSG

#---------------------------------------------------#
# Lambda Function
#---------------------------------------------------#
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub ${Prefix}-function
      Role: !GetAtt LambdaFunctionRole.Arn
      Runtime: python3.10
      Handler: index.lambda_handler
      Code:
        ZipFile: |
          import json
          import boto3
          from datetime import datetime
          
          def lambda_handler(event, context):
              s3 = boto3.client(&#39;s3&#39;)
              bucket_name = &#39;sample-check-network-status-bucket&#39;
              current_time = datetime.now().strftime(&#39;%Y%m%d_%H%M%S&#39;)
              file_name = f&#39;trial_{current_time}.txt&#39;
              file_content = &#39;Hello World!&#39;
              
              s3.put_object(Bucket=bucket_name, Key=file_name, Body=file_content)
              return {
                  &#39;statusCode&#39;: 200,
                  &#39;body&#39;: json.dumps(&#39;Hello from Lambda!&#39;)
              }
      VpcConfig:
        SecurityGroupIds:
          - !Ref LambdaSG
        SubnetIds:
          - !Ref ParamPrivate1ASubnetId
      Timeout: 10

#---------------------------------------------------#
# S3 Bucket for checking the network status
#---------------------------------------------------#
  AssetsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${Prefix}-check-network-status-bucket
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      VersioningConfiguration:
        Status: Suspended</code></pre></div>



<h3 class="wp-block-heading"><span id="toc4">状況を再現(スタックを作成する)</span></h3>



<p>ここでは、CloudShellからスタック作成を実施します。</p>



<p>(破壊した環境にもよりますが、おそらく通常では開発環境等で復旧テスト→当該環境で復旧作業となると想定しています。その際に余計な環境差を出さないためにもCloudShellからの実行としています。)</p>



<p>2つのスタックには依存関係があるので、Network.yaml → Lambda.yamlの順でデプロイしていきます。</p>



<p>ファイルをCloudShellにアップロードした後、以下のコマンドを実行します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code># 1. ネットワークスタックを作成
aws cloudformation deploy \
    --template-file Network.yaml \
    --stack-name  sample-network-stack

# 2. Lambdaスタックを作成
aws cloudformation deploy \
    --template-file Lambda.yaml \
    --stack-name  sample-lambda-stack \
    --capabilities CAPABILITY_NAMED_IAM

# 3. ネットワークスタックの管理下にあるリソースを確認
aws cloudformation describe-stack-resources \
    --stack-name sample-network-stack \
    --query &quot;join(&#39;, &#39;, StackResources[*].LogicalResourceId)&quot; --output text \
    --output text</code></pre></div>



<p>3の実行結果は以下となります。</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" width="1024" height="19" src="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-02-1024x19.png" alt="" class="wp-image-204" style="width:1078px;height:auto" srcset="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-02-1024x19.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-02-300x6.png 300w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-02-768x14.png 768w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-02.png 1062w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p></p>



<p>また、Lambda関数を実行してS3バケットにファイルが出力できることも確認しておきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code># 1. S3バケット内のオブジェクト数を確認
#    → 「Total Objects: 0」と表示される
aws s3 ls s3://sample-check-network-status-bucket \
    --recursive \
    --summarize

# 2. Lambda関数を実行
aws lambda invoke \
    --function-name sample-function \
    outputfile.txt

# 3. S3バケット内のオブジェクト数を確認
#    → 「Total Objects: 1」と表示される
aws s3 ls s3://sample-check-network-status-bucket\
    --recursive \
    --summarize</code></pre></div>



<p>ここまででスタックの作成はOKです。</p>



<p></p>



<h3 class="wp-block-heading"><span id="toc5">状況を再現(スタック管理下からサブネットを外す)</span></h3>



<p>では、ここからはサブネットをスタック管理下から外していきます。</p>



<p>Network.yaml内の記述を修正したDestructive-Network.yamlを用意して、既存のスタックを上書きしましょう。</p>



<p>Destructive-Network.yamlで作成予定のリソースは以下です。</p>



<ul class="wp-block-list">
<li>VPC</li>



<li>ルートテーブル</li>
</ul>



<p>サブネットを削除しているのがポイントです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="Destructive-Network.yaml"><code>AWSTemplateFormatVersion: &quot;2010-09-09&quot;
Description: Network

Parameters:
  Prefix:
    Description: Enter Prefix.
    Type: String
    Default: sample

Resources:
#---------------------------------------------------#
# VPC
#---------------------------------------------------#
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-vpc

#---------------------------------------------------#
# RouteTable
#---------------------------------------------------#
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-private-rt</code></pre></div>



<p></p>



<p>ではこのテンプレートをCloudShellにアップロードして、デプロイしちゃいます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash" data-show-lang="1"><code>aws cloudformation deploy \
    --template-file Destructive-Network.yaml \
    --stack-name  sample-network-stack</code></pre></div>



<p></p>



<p>1時間程度経過した後にコンソール画面を確認すると、サブネットの依存関係の問題で数回DELETE_FAILEDとなり、最終的にエラー文は出力されていはいるもののUPDATE_COMPLETEステータスとなっています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code># DELETE_FAILED時のエラー文
Resource handler returned message: &quot;The subnet &#39;subnet-0dxxxxxxxxxxxxx25&#39; has dependencies and cannot be deleted.

# UPDATE_COMPLETE時の文
Update successful. One or more resources could not be deleted.</code></pre></div>



<p></p>



<p>ここで、ネットワークスタック管理下にあったサブネットの状況を確認します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code># 1. ネットワークスタックの管理下にあるリソースを確認
aws cloudformation describe-stack-resources \
    --stack-name sample-network-stack \
    --query &quot;join(&#39;, &#39;, StackResources[*].LogicalResourceId)&quot; --output text \
    --output text

# 2. Nameを指定して、既存のサブネットから対象のサブネットの情報を表示
aws ec2 describe-subnets \
    --query &quot;Subnets[?Tags[?Key==&#39;Name&#39; && Value==&#39;sample-private-subnet-1a&#39;]]&quot;</code></pre></div>



<p>1の実行結果は以下です。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="298" height="14" src="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-03.png" alt="" class="wp-image-207"/></figure>



<p>サブネットはスタック管理下にはいないことが伺えます。</p>



<p>また一方で、2の結果からはサブネットは削除されず環境上に存在しており、たんにスタックから外れているだけだと推測できます。</p>



<p>この状況ですと、今後の運用でNetwork.yamlテンプレート内でサブネットのID等を利用したい場合には値をべた書きすることになります。仮にこのテンプレートを1環境だけで利用する場合はこれでも構いませんが、複数環境で利用している場合には!Ref等で値を渡せないのはかなり厳しいです。</p>



<p></p>



<p>そのため、再度サブネットをスタック管理下に引き戻し、元の状態へ戻す必要がでてくるはずです。</p>



<h3 class="wp-block-heading"><span id="toc6">本題：サブネットをスタック管理下に引き戻す</span></h3>



<p>サブネットをスタック管理下に引き戻すためには、既存スタック(sample-network-stack)に対象のサブネットをインポートします。</p>



<p>既存スタックへのインポート機能の詳細については、以下の公式ドキュメントを参照ください。</p>



<p><a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/resource-import-existing-stack.html#resource-import-existing-stack-cli">スタックへの既存リソースのインポート &#8211; AWS CloudFormation (amazon.com)</a></p>



<p></p>



<p>では、順を追ってインポートを実行していきましょう。</p>



<h4 class="wp-block-heading"><span id="toc7">1. インポート時の設定ファイルを作成する</span></h4>



<p>インポートコマンド実行時に必要になる設定ファイルを組み立てていきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="Import.json"><code>[
    {
        &quot;ResourceType&quot;: &quot;AWS::EC2::Subnet&quot;,
        &quot;LogicalResourceId&quot;: &quot;PrivateSubnet1A&quot;,
        &quot;ResourceIdentifier&quot;: {
            &quot;SubnetId&quot;:&quot;subnet-0dxxxxxxxxxxxxx25&quot;
        }
    }
]</code></pre></div>



<p>各種キーについて簡単に説明します。</p>



<ul class="wp-block-list">
<li>ResourceType
<ul class="wp-block-list">
<li>インポート対象のリソースタイプを指定する</li>
</ul>
</li>



<li>LogicalResourceId
<ul class="wp-block-list">
<li>インポート時に読み込ませるCloudFormationテンプレート上で、インポート対象のリソースに割り当てる論理IDを指定する</li>



<li>この記事では最終的に元のNetwork.yamlで上書きするため、そこで使用していた論理IDを使っています</li>
</ul>
</li>



<li>ResourceIdentifier
<ul class="wp-block-list">
<li>インポート対象リソース識別子を指定する</li>
</ul>
</li>
</ul>



<p>また、それぞれのキーに何を指定すればよいか(ここではSubnetId)は、get-tempate-summeryコマンドで大まかに調査可能です。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>aws cloudformation get-template-summary \
    --template-body file://./Network.yaml \
    --query &#39;ResourceIdentifierSummaries&#39;</code></pre></div>



<p></p>



<h4 class="wp-block-heading"><span id="toc8">2. インポート用のCloudFormationテンプレートを作成する</span></h4>



<p>ここでは、現在デプロイされているCloudFormationテンプレートにインポート対象とするサブネットを追記したものを作成します。</p>



<p>次の2点に注意してください。</p>



<ul class="wp-block-list">
<li>既存リソースの設定値は変更しない</li>



<li>インポート対象のリソースには、DeletionPolicyとUpdateReplacePolicyを追記する</li>
</ul>



<p>※その他の削除済みリソースは後ほど再作成します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="Import-Network.yaml" data-line="39-51"><code>AWSTemplateFormatVersion: &quot;2010-09-09&quot;
Description: Network

Parameters:
  Prefix:
    Description: Enter Prefix.
    Type: String
    Default: sample

Resources:
#---------------------------------------------------#
# VPC
#---------------------------------------------------#
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-vpc

#---------------------------------------------------#
# RouteTable
#---------------------------------------------------#
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-private-rt

#---------------------------------------------------#
# Subnet1A
#---------------------------------------------------#
  PrivateSubnet1A:
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.16.0/20
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref AWS::Region
      Tags:
        - Key: Name
          Value: !Sub ${Prefix}-private-subnet-1a</code></pre></div>



<p></p>



<h4 class="wp-block-heading"><span id="toc9">3. インポート実行</span></h4>



<p>インポートは次の3ステップで実行していきます。</p>



<ol class="wp-block-list">
<li>変更セットの作成 (CLIコマンド：create-change-set)</li>



<li>変更セットの確認 (CLIコマンド：describe-change-set)</li>



<li>変更セットの実行 (CLIコマンド：execute-change-set)</li>
</ol>



<p>Import.jsonとImport-Network.yamlをCloudShellにアップロードし、コマンドを実行します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code># 1. 変更セットの作成
aws cloudformation create-change-set \
    --stack-name sample-network-stack --change-set-name sample-network-change-set \
    --change-set-type IMPORT \
    --resources-to-import file://Import.json \
    --template-body file://Import-Network.yaml

# 2. 変更セットの確認
aws cloudformation describe-change-set \
    --stack-name sample-network-stack \
    --change-set-name sample-network-change-set

# 3. 変更セットの実行
aws cloudformation execute-change-set \
    --stack-name sample-network-stack \
    --change-set-name sample-network-change-set</code></pre></div>



<p></p>



<p>サブネットが再びスタック管理下となったことを確認します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code># ネットワークスタックの管理下にあるリソースを確認
aws cloudformation describe-stack-resources \
    --stack-name sample-network-stack \
    --query &quot;join(&#39;, &#39;, StackResources[*].LogicalResourceId)&quot; --output text \
    --output text</code></pre></div>



<p>無事に成功しました。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="344" height="17" src="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-04.png" alt="" class="wp-image-214" srcset="https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-04.png 344w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-04-300x15.png 300w, https://tech-itoya.com/wp-content/uploads/2024/10/danto-20241013-04-320x17.png 320w" sizes="(max-width: 344px) 100vw, 344px" /></figure>



<p></p>



<h4 class="wp-block-heading"><span id="toc10">4. 削除してしまったその他のリソースを再作成する</span></h4>



<p>ここまでくればあと一息です。</p>



<p>最初のスタックではVPCエンドポイントなども管理していましたが、Destructive-Network.yamlで上書きした際にこれらのリソースは削除されてしまいました。</p>



<p>ですので、最後の手順として元のNetwork.yamlでデプロイして元の状態に戻しましょう。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code># 1. ネットワークスタックを作成
aws cloudformation deploy \
    --template-file Network.yaml \
    --stack-name  sample-network-stack

# 2. ネットワークスタックの管理下にあるリソースを確認
aws cloudformation describe-stack-resources \
    --stack-name sample-network-stack \
    --query &quot;join(&#39;, &#39;, StackResources[*].LogicalResourceId)&quot; --output text \
    --output text</code></pre></div>



<p></p>



<h4 class="wp-block-heading"><span id="toc11">5. 復旧を確認</span></h4>



<p>最後にLambda関数を実行して、ネットワークの疎通に問題がないことを確認して終わりとします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code># 1. Lambda関数を実行
aws lambda invoke \
    --function-name sample-function \
    outputfile.txt

# 2. S3バケット内のオブジェクト数を確認
#    → 「Total Objects: 2」と表示される
aws s3 ls s3://sample-check-network-status-bucket\
    --recursive \
    --summarize</code></pre></div>



<h2 class="wp-block-heading"><span id="toc12">おまけ + 参考文献</span></h2>



<h3 class="wp-block-heading"><span id="toc13">おまけ</span></h3>



<p>VPC Lambdaを含むスタックを削除しようとすると、ENIが引っかかって削除に時間がかかることがあります。(私の環境では25分ほどかかりました。)</p>



<p>そんなときのTipsとして、先にCLI操作によってLambdaの設定からVPC設定を外してしまいましょう。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash" data-show-lang="1"><code># 1. S3バケットを空にする
aws s3 rm s3://sample-check-network-status-bucket --recursive

# 2. Lambda関数からVPC設定を外す
aws lambda update-function-configuration \
    --function-name sample-function \
    --vpc-config SubnetIds=[],SecurityGroupIds=[]

# 3. Lambdaスタックを削除
aws cloudformation delete-stack \
    --stack-name sample-lambda-stack

# 4. ネットワークスタックを削除(※依存関係があるため、Lambdaスタックが削除されたことを確認してから実行)
aws cloudformation delete-stack \
    --stack-name sample-network-stack</code></pre></div>



<p>以上です。</p>



<h3 class="wp-block-heading"><span id="toc14">参考文献</span></h3>



<ul class="wp-block-list">
<li><a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/resource-import-existing-stack.html#resource-import-existing-stack-cli">スタックへの既存リソースのインポート &#8211; AWS CloudFormation (amazon.com)</a>
<ul class="wp-block-list">
<li>公式ドキュメント</li>
</ul>
</li>



<li><a href="https://qiita.com/a_b_/items/466ba74cc28efcb11285">CloudFormationのresource importを試してみた #AWS &#8211; Qiita</a>
<ul class="wp-block-list">
<li>スタックへのインポートについて紹介しているQiita記事</li>
</ul>
</li>
</ul>
		<div class="wpulike wpulike-heart " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="いいねボタン"
					data-ulike-id="134"
					data-ulike-nonce="16282860fa"
					data-ulike-type="post"
					data-ulike-template="wpulike-heart"
					data-ulike-display-likers=""
					data-ulike-likers-style="popover"
					class="wp_ulike_btn wp_ulike_put_image wp_post_btn_134"></button><span class="count-box wp_ulike_counter_up" data-ulike-counter-value="+1"></span>			</div></div>
	]]></content:encoded>
					
					<wfw:commentRss>https://tech-itoya.com/cloudformation%e3%82%b9%e3%82%bf%e3%83%83%e3%82%af%e3%81%8b%e3%82%89%e5%a4%96%e3%82%8c%e3%81%9f%e3%82%b5%e3%83%96%e3%83%8d%e3%83%83%e3%83%88%e3%82%92%e3%82%b9%e3%82%bf%e3%83%83%e3%82%af%e7%ae%a1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
