<?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>AWS | 伊東屋TECHブログ</title>
	<atom:link href="https://tech-itoya.com/tag/aws/feed/" rel="self" type="application/rss+xml" />
	<link>https://tech-itoya.com</link>
	<description>千葉県・船橋市の総合園芸店が運営する技術ブログ</description>
	<lastBuildDate>Wed, 29 Oct 2025 14:43:58 +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>AWS | 伊東屋TECHブログ</title>
	<link>https://tech-itoya.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Cloudfront を導入時はレートリミットにも気をつけよう</title>
		<link>https://tech-itoya.com/cloudfront-%e3%82%92%e5%b0%8e%e5%85%a5%e6%99%82%e3%81%af%e3%83%ac%e3%83%bc%e3%83%88%e3%83%aa%e3%83%9f%e3%83%83%e3%83%88%e3%82%82%e6%b0%97%e3%82%92%e3%81%a4%e3%81%91%e3%82%88%e3%81%86/</link>
					<comments>https://tech-itoya.com/cloudfront-%e3%82%92%e5%b0%8e%e5%85%a5%e6%99%82%e3%81%af%e3%83%ac%e3%83%bc%e3%83%88%e3%83%aa%e3%83%9f%e3%83%83%e3%83%88%e3%82%82%e6%b0%97%e3%82%92%e3%81%a4%e3%81%91%e3%82%88%e3%81%86/#respond</comments>
		
		<dc:creator><![CDATA[tori-dash]]></dc:creator>
		<pubDate>Sat, 04 Oct 2025 15:28:51 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Cloudfront]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=318</guid>

					<description><![CDATA[目次 はじめに構成図4XX 系エラーの原因原因1. WAF の Ratelimit による 403 エラー原因2. アプリケーション (laravel) API Throttle による 429 エラーCloudfron [&#8230;]]]></description>
										<content:encoded><![CDATA[

  <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">はじめに</a></li><li><a href="#toc2" tabindex="0">構成図</a></li><li><a href="#toc3" tabindex="0">4XX 系エラーの原因</a></li><li><a href="#toc4" tabindex="0">原因1. WAF の Ratelimit による 403 エラー</a></li><li><a href="#toc5" tabindex="0">原因2. アプリケーション (laravel) API Throttle  による 429 エラー</a><ol><li><a href="#toc6" tabindex="0">Cloudfront で、CloudFront-Viewer-Address をヘッダーを付与</a></li><li><a href="#toc7" tabindex="0">ApacheでCloudFront-Viewer-Addressを受け取り、RemoteIP に設定</a></li></ol></li><li><a href="#toc8" tabindex="0">まとめ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">はじめに</span></h2>



<p>先日ALBの前段にCloudFrontを導入した際に、4XX系のエラーが爆増する現象に遭遇しました</p>



<p>開発環境での確認段階で気づけたのでセーフでしたが、ある程度リクエスト量をかけないと再現しない現象で見落としそうになったため、備忘録を残しておきます</p>



<p>同様の構成を検討されている方々の参考になれば幸いです。</p>



<h2 class="wp-block-heading"><span id="toc2">構成図</span></h2>



<p>今回想定しているインフラの構成は以下です</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="882" height="224" src="https://tech-itoya.com/wp-content/uploads/2025/05/image.png" alt="" class="wp-image-330" srcset="https://tech-itoya.com/wp-content/uploads/2025/05/image.png 882w, https://tech-itoya.com/wp-content/uploads/2025/05/image-300x76.png 300w, https://tech-itoya.com/wp-content/uploads/2025/05/image-768x195.png 768w" sizes="(max-width: 882px) 100vw, 882px" /></figure>



<p>なお、Cloudfront の後ろに WAF を配置した理由は、<strong> CDNキャッシュ後にWAFを通すことでWAFルールが検査するリクエスト数が減る</strong>のでコストメリットがありそうと考えたためです。</p>



<h2 class="wp-block-heading"><span id="toc3">4XX 系エラーの原因</span></h2>



<p>Cloudfront 導入後に 4XX 系エラーが増加してしまった原因は大きく 2 つありました</p>



<ol class="wp-block-list">
<li><span class="marker-under"><strong>WAFのRatelimitルールによるブロック</strong></span></li>



<li><span class="marker-under"><strong>アプリケーション(PHP, Laravel)のRatelimit</strong></span></li>
</ol>



<p>両方に共通する根本原因は、Cloudfrontを経由することで、WAFやアプリケーションから見たクライアントのIPアドレスが、実際のユーザーのIPではなくCloudfrontのエッジサーバーのIPアドレスに置き換わってしまったことでした。</p>



<p>図解すると、以前は以下のように User IP を直接とれていましたが</p>



<figure class="wp-block-image size-full"><img decoding="async" width="878" height="216" src="https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-1.png" alt="" class="wp-image-323" srcset="https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-1.png 878w, https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-1-300x74.png 300w, https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-1-768x189.png 768w" sizes="(max-width: 878px) 100vw, 878px" /></figure>



<p>Cloudfront導入後は IP が置換されてしまうため、正しいクライアントIPが認識できなくなり、多数のリクエストがレートリミットでブロックされてしまいました。</p>



<figure class="wp-block-image size-full"><img decoding="async" width="855" height="204" src="https://tech-itoya.com/wp-content/uploads/2025/05/スクリーンショット-2025-05-05-104110.png" alt="" class="wp-image-329" srcset="https://tech-itoya.com/wp-content/uploads/2025/05/スクリーンショット-2025-05-05-104110.png 855w, https://tech-itoya.com/wp-content/uploads/2025/05/スクリーンショット-2025-05-05-104110-300x72.png 300w, https://tech-itoya.com/wp-content/uploads/2025/05/スクリーンショット-2025-05-05-104110-768x183.png 768w" sizes="(max-width: 855px) 100vw, 855px" /></figure>



<h2 class="wp-block-heading"><span id="toc4">原因1. WAF の Ratelimit による 403 エラー</span></h2>



<p>400 系エラーが増えた 1 つ目の原因は WAF によるレート制限です</p>



<p>WAFには、DDos的なアクセスへの対策として、同一IPからの一定レートを超えるアクセスはブロックするルールが設定されていました</p>



<p>ただ構成図で示した通り、今回はWAFの前段にCloudfrontがいるため、何も意識をしないと Cloudfront の IP を見てレート制限をかけてしまいます</p>



<p>対策としては、WAFのレート制限はIPアドレスをどの値で集計するか選ぶことが出来るため、<strong>Source IP を使った集計ではなく、<code>X-Fowarded-For</code> ヘッダーの値から集計するように設定する</strong>ことで、クライアントのIPを正しく判定できるようになりました。</p>



<p>コンソールから設定する場合、<strong><code>リクエストの集約</code>を [ヘッダー内のIPアドレス]、<code>ヘッダーフィールド名</code> を「X-Forwarded-For」に設定</strong>することで、Cloudfrontの前段にあるIPアドレスを見てレート制限をかけることができます</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="797" height="807" src="https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-3.png" alt="" class="wp-image-331" srcset="https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-3.png 797w, https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-3-296x300.png 296w, https://tech-itoya.com/wp-content/uploads/2025/05/tori-20250505-3-768x778.png 768w" sizes="(max-width: 797px) 100vw, 797px" /></figure>



<h2 class="wp-block-heading"><span id="toc5">原因2. アプリケーション (laravel) API Throttle  による 429 エラー</span></h2>



<p>Webアプリケーション側でも、IPベースでのAPIのレート制限機能を持っています。<br>今回の環境でも、Laravel の API Throttle 機能が有効化されており、アプリケーションレイヤーでも 429 エラーを返していました</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>API Throttleの参考: <a href="https://kinsta.com/jp/blog/laravel-throttle/">https://kinsta.com/jp/blog/laravel-throttle/</a></p>
</blockquote>



<p>そこで、対策として以下の設定を追加しました</p>



<ol class="wp-block-list">
<li><span class="marker-under"><strong>Cloudfront で、CloudFront-Viewer-Address をヘッダーを付与して後続のALBに流す</strong></span></li>



<li><span class="marker-under"><strong>Apche で CloudFront-Viewer-Address を受け取り、RemoteIP として扱う設定を追加する</strong></span></li>
</ol>



<h3 class="wp-block-heading"><span id="toc6">Cloudfront で、CloudFront-Viewer-Address をヘッダーを付与</span></h3>



<p>Cloudfront では後続のALBになどにHTTPリクエストをリレーする際、追加ヘッダーを設定することができ、そのうちの一つに「CloudFront-Viewer-Address」というヘッダーがあります。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><code>CloudFront-Viewer-Address</code>&nbsp;– ビューワーの IP アドレスと、リクエストのソースポートを示します。例えば、<code>198.51.100.10:46532</code>&nbsp;のヘッダー値は、ビューワーの IP アドレスが 198.51.100.10 で、リクエストのソースポートが 46532 であることを意味します。</p>
</blockquote>





<a rel="noopener" href="https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/adding-cloudfront-headers.html" title="CloudFront &#12398;&#12522;&#12463;&#12456;&#12473;&#12488;&#12504;&#12483;&#12480;&#12540;&#12434;&#36861;&#21152;&#12377;&#12427; - Amazon CloudFront" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2FAmazonCloudFront%2Flatest%2FDeveloperGuide%2Fadding-cloudfront-headers.html?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">CloudFront &#12398;&#12522;&#12463;&#12456;&#12473;&#12488;&#12504;&#12483;&#12480;&#12540;&#12434;&#36861;&#21152;&#12377;&#12427; - Amazon CloudFront</div><div class="blogcard-snippet external-blogcard-snippet">CloudFront HTTP リクエストヘッダーを追加して、ビューワーのデバイスタイプ、IP アドレス、地理的位置、リクエストプロトコル (HTTP または HTTPS)、HTTP バージョン、TLS 接続の詳細、および JA4 フィンガ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/adding-cloudfront-headers.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">docs.aws.amazon.com</div></div></div></div></a>




<p>これを使えば後続処理に、実際のリクエスト元の IP を渡すことができます</p>



<p>以下のようなオリジンリクエストポリシーを作成し、対象のビヘイビアに紐づけることで、「CloudFront-Viewer-Address」ヘッダーを後続に送ることができます。</p>



<ul class="wp-block-list">
<li><strong>ポリシー名等: 任意</strong></li>



<li><strong>ヘッダー: すべてのビューワーヘッダーと次の Cloudfront ヘッダー</strong></li>



<li><strong>add header: CloudFront-Viewer-Address</strong></li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="429" src="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251004-1-1024x429.png" alt="" class="wp-image-335" srcset="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251004-1-1024x429.png 1024w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251004-1-300x126.png 300w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251004-1-768x322.png 768w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251004-1-1536x644.png 1536w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251004-1.png 1811w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading"><span id="toc7">ApacheでCloudFront-Viewer-Addressを受け取り、RemoteIP に設定</span></h3>



<p>上記ステップにより、Cloudfront から CloudFront-Viewer-Address ヘッダーが送られてきます。</p>



<p>これを Apache 側で RemoteIP としてセットしてあげることで、PHPなどアプリケーション層でも正しいIPを認識できるようになります。</p>



<p>CloudFront-Viewer-Address は形式に少し癖があり、<code><strong>198.51.100.10:46532</strong></code> のように IP アドレスの後ろにポート番号がついてしまうため、これを取り除いてあげる必要があります。</p>



<p>apache の httpd.conf ファイルに以下を追加してあげることで、Remote IP に正しいIPが設定されました。</p>



<pre class="wp-block-code"><code>... (もとの設定) ...

# CloudFront-Viewer-Address から IP 部分のみを抽出 (例: CloudFront-Viewer-Address: 192.30.252.129:443)
SetEnvIf CloudFront-Viewer-Address "^(&#91;0-9.]+):" VIEWER_IP=$1

# 抽出した IP アドレスを RemoteIPHeader として使用
RequestHeader set X-VIEWER-IP %{VIEWER_IP}e env=VIEWER_IP
RemoteIPHeader X-VIEWER-IP

... (もとの設定) ...</code></pre>



<h2 class="wp-block-heading"><span id="toc8">まとめ</span></h2>



<p>Cloudfront 導入時などはIPがつけ変わるため、後段のアプリケーションやWAFなどでIP制限をかけている場合はそこにも注意が必要だよというお話でした。</p>
		<div class="wpulike wpulike-heart " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="いいねボタン"
					data-ulike-id="318"
					data-ulike-nonce="d63181ca08"
					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_318"></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/cloudfront-%e3%82%92%e5%b0%8e%e5%85%a5%e6%99%82%e3%81%af%e3%83%ac%e3%83%bc%e3%83%88%e3%83%aa%e3%83%9f%e3%83%83%e3%83%88%e3%82%82%e6%b0%97%e3%82%92%e3%81%a4%e3%81%91%e3%82%88%e3%81%86/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SSOユーザーごとにS3アクセスを制御するバケットポリシーの書き方</title>
		<link>https://tech-itoya.com/identity-center%e3%81%a7sso%e3%81%97%e3%81%9f%e5%80%8b%e5%88%a5%e3%83%a6%e3%83%bc%e3%82%b6%e3%83%bc%e3%81%94%e3%81%a8%e3%81%abs3%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%82%92%e5%88%b6%e5%be%a1%e3%81%99/</link>
					<comments>https://tech-itoya.com/identity-center%e3%81%a7sso%e3%81%97%e3%81%9f%e5%80%8b%e5%88%a5%e3%83%a6%e3%83%bc%e3%82%b6%e3%83%bc%e3%81%94%e3%81%a8%e3%81%abs3%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%82%92%e5%88%b6%e5%be%a1%e3%81%99/#respond</comments>
		
		<dc:creator><![CDATA[tori-dash]]></dc:creator>
		<pubDate>Sat, 28 Dec 2024 01:35:49 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[Identity Center]]></category>
		<category><![CDATA[S3]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=285</guid>

					<description><![CDATA[こんにちはとりです。 今回はIdentiy Centerで管理しているSSOユーザーごとにS3アクセスを制御したいときのバケットポリシーの書き方について記事になります。 ポリシー自体はシンプルになったのですが、どの条件で [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんにちはとりです。</p>



<p>今回はIdentiy Centerで管理しているSSOユーザーごとにS3アクセスを制御したいときのバケットポリシーの書き方について記事になります。</p>



<p>ポリシー自体はシンプルになったのですが、どの条件でコントロールすればうまくいくか案外迷ったので、同じような方の参考になれば幸いです。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">想定している構成</a></li><li><a href="#toc2" tabindex="0">ポリシーの書き方</a></li><li><a href="#toc3" tabindex="0">おまけ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">想定している構成</span></h2>



<p>細かい要件としては以下を想定しています</p>



<ul class="wp-block-list">
<li>AWS 利用者は <strong>authority-A </strong>という許可セットを使って、対象のAWSアカウントにアクセスする</li>



<li><strong>authority-A</strong> は対象AWSアカウント内のS3全般に対して広めのアクセス権限をもっている</li>



<li>AWSアカウント内の特定のS3バケットだけは、特定ユーザーだけ見れるようにしたい。</li>
</ul>



<p>SSOの許可セットで広めに AWS 権限を与えているものの、特定のS3バケットについては見れるユーザーを絞りたいというケースになります</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="551" height="182" src="https://tech-itoya.com/wp-content/uploads/2024/12/tori-20241228-01-1.png" alt="" class="wp-image-295" style="width:702px;height:auto" srcset="https://tech-itoya.com/wp-content/uploads/2024/12/tori-20241228-01-1.png 551w, https://tech-itoya.com/wp-content/uploads/2024/12/tori-20241228-01-1-300x99.png 300w" sizes="(max-width: 551px) 100vw, 551px" /></figure>



<h2 class="wp-block-heading"><span id="toc2">ポリシーの書き方</span></h2>



<p>結果シンプルになりましたが、以下のようなポリシーで実現できました</p>



<p>S3に限らずIAM全般では明示的な Deny がある場合、それが最初に評価されるため、許可セット(<strong>authority-A</strong>)がアクセス権限を持っていたとしても、Deny 条件にマッチすればアクセスできなくなります。</p>



<pre class="wp-block-code"><code>{
	"Version": "2012-10-17",
	"Statement": &#91;
		{
			"Sid": "SSOTest",
			"Effect": "Deny",
			"Principal": "*",
			"Action": "*",
			"Resource": &#91;
                             "arn:aws:s3:::&lt;S3 バケット名&gt;/*",
                             "arn:aws:s3:::&lt;S3 バケット名&gt;"
                        ],
			"Condition": {
				"StringNotLike": {
					"aws:userId": "AROA*:&lt;SSO ユーザー名&gt;*"
				}
			}
		}
	]
}</code></pre>



<p>SSO ユーザー名を確認したい場合は、以下コマンドを叩いて確認できます</p>



<pre class="wp-block-code"><code>aws sts get-caller-identity --output yaml</code></pre>



<p>注意点としては先ほど書いたように、Deny 句があると最初にそれが評価されるため、SSOユーザー以外にもアクセスしてくるプリンシパル(例えばECSやlambda)がいる場合、適宜 <strong>Condition</strong> 句でdeny対象から外す必要があります。</p>



<h2 class="wp-block-heading"><span id="toc3">おまけ</span></h2>



<p>最初やろうとしてダメだった方法も書いておきます</p>



<p>SSO から 払いだされるフェデレーテッドユーザーの形式も <strong>&lt;許可セット名&gt;/&lt;SSO ユーザー名&gt; </strong>で、SSOユーザー名含んでいたので、これでポリシーかけないかな？と思ったのですがダメそうでした。</p>



<p>↓の感じで条件書いてみたのですが、許可セットまでは制御がきくものの SSO ユーザー名までは <code>aws:PrincipalARN</code> に含められないようです</p>



<pre class="wp-block-code"><code>"Condition": {
	"StringNotLike": {
		"aws:PrincipalARN": "arn:aws:iam::*:role/aws-reserved/sso.amazonaws.com/*/AWSReservedSSO_&lt;許可セット名&gt;/&lt;SSO ユーザー名&gt;"
        }
}</code></pre>



<p></p>
		<div class="wpulike wpulike-heart " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="いいねボタン"
					data-ulike-id="285"
					data-ulike-nonce="a5829a97a1"
					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_285"></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/identity-center%e3%81%a7sso%e3%81%97%e3%81%9f%e5%80%8b%e5%88%a5%e3%83%a6%e3%83%bc%e3%82%b6%e3%83%bc%e3%81%94%e3%81%a8%e3%81%abs3%e3%82%a2%e3%82%af%e3%82%bb%e3%82%b9%e3%82%92%e5%88%b6%e5%be%a1%e3%81%99/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>各リージョンで有効化したGuardDutyの検出結果を管理者に通知する</title>
		<link>https://tech-itoya.com/%e5%90%84%e3%83%aa%e3%83%bc%e3%82%b8%e3%83%a7%e3%83%b3%e3%81%a7%e6%9c%89%e5%8a%b9%e5%8c%96%e3%81%97%e3%81%9fguardduty%e3%81%ae%e6%a4%9c%e5%87%ba%e7%b5%90%e6%9e%9c%e3%82%92%e7%ae%a1%e7%90%86%e8%80%85/</link>
					<comments>https://tech-itoya.com/%e5%90%84%e3%83%aa%e3%83%bc%e3%82%b8%e3%83%a7%e3%83%b3%e3%81%a7%e6%9c%89%e5%8a%b9%e5%8c%96%e3%81%97%e3%81%9fguardduty%e3%81%ae%e6%a4%9c%e5%87%ba%e7%b5%90%e6%9e%9c%e3%82%92%e7%ae%a1%e7%90%86%e8%80%85/#respond</comments>
		
		<dc:creator><![CDATA[アーサー・C・ダントー]]></dc:creator>
		<pubDate>Fri, 06 Dec 2024 14:33:46 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[AWS]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=241</guid>

					<description><![CDATA[こんにちは。ダントーです。 今回は、セキュリティ施策のために東京リージョン以外の各リージョンでGuardDurtyを有効化したけど検出結果をどうやって管理者に通知するかで悩んでいる方向けの記事です。 簡素ではありますが、 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんにちは。ダントーです。</p>



<p>今回は、セキュリティ施策のために東京リージョン以外の各リージョンでGuardDurtyを有効化したけど検出結果をどうやって管理者に通知するかで悩んでいる方向けの記事です。</p>



<p>簡素ではありますが、いくつかある選択肢を比較考量していく中で自分の環境に最適な構成を選ぶ材料となれば幸いです。</p>



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



<p></p>



<p><strong>想定検索ワード</strong>：<span class="marker-under">GuardDuty 検出結果 通知 東京リージョン以外</span></p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</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">1. 可能なオプションについて考える</a><ol><li><a href="#toc3" tabindex="0">① 各リージョンにEventBridgeルールとSNSトピックを作成する</a></li><li><a href="#toc4" tabindex="0">② 東京リージョンCustom event busで集約する</a></li><li><a href="#toc5" tabindex="0">③ AWS Security Hubのリージョン集約を有効化する</a></li></ol></li><li><a href="#toc6" tabindex="0">2. 参考記事</a></li></ol>
    </div>
  </div>

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



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="311" src="https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-02-1024x311.png" alt="" class="wp-image-263" srcset="https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-02-1024x311.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-02-300x91.png 300w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-02-768x233.png 768w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-02.png 1335w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading"><span id="toc2">1. 可能なオプションについて考える</span></h2>



<p>まずは各リージョンの検出結果を管理者に通知するにあたって、どのようなオプションが可能なのか考えていきます。</p>



<h3 class="wp-block-heading"><span id="toc3">① 各リージョンにEventBridgeルールとSNSトピックを作成する</span></h3>



<p>簡単な構成図を書くとこのようになります。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1002" height="584" src="https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-1.png" alt="" class="wp-image-244" srcset="https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-1.png 1002w, https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-1-300x175.png 300w, https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-1-768x448.png 768w" sizes="(max-width: 1002px) 100vw, 1002px" /></figure>



<p>かなり愚直な選択肢ではありますが、構成上の統一感という意味ではこの選択肢も悪くないのかもしれません。</p>



<p>しかし各リージョンにEventBridgeルールやSNSトピックを作る必要があり、またSNSトピックについては都度サブスクライブを行わなくてはいけない点はデメリットとなります。</p>



<p>加えて、検出結果をフィルタリング&amp;整形しようとすると各リージョンのEventBridgeルールに同一設定を入れなくてはいけない点もやや微妙と言えるでしょう。</p>



<p>まとめると&#8230;</p>



<p><strong><span class="marker-under"><span class="marker-under-blue">メリット</span></span></strong></p>



<ul class="wp-block-list">
<li>構成上の統一感あり</li>



<li>構築自体の難易度は低め(同一設定の繰り返しとなるためとっつきやすい)</li>
</ul>



<p><span class="marker-under"><strong><span class="marker-under-red">デメリット</span></strong></span></p>



<ul class="wp-block-list">
<li>SNSを各リージョンごとにサブスクライブしなくてはいけない</li>



<li>リソースの作成が冗長的になる(同一リソース・同一設定の繰り返しとなるため)</li>
</ul>



<h3 class="wp-block-heading"><span id="toc4">② 東京リージョンCustom event busで集約する</span></h3>



<p>先ほどの①のパターンから東京リージョン以外のSNSトピックを削除します。</p>



<p>つまり、全リージョンの検出結果を一度東京リージョンに集約し、東京リージョンのSNSトピックから管理者に通知を行います。</p>



<p>構成図としては以下のようになります。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="591" src="https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-2-2-1024x591.png" alt="" class="wp-image-249" srcset="https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-2-2-1024x591.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-2-2-300x173.png 300w, https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-2-2-768x443.png 768w, https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-2-2-120x68.png 120w, https://tech-itoya.com/wp-content/uploads/2024/11/danto-20241123-2-2.png 1109w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>フローとしては、全リージョン(東京リージョンを含む)での検出結果を東京リージョンのカスタムイベントバスに送信し、カスタムイベントバスに紐づけたルールでファイルタリング&amp;整形を行います。</p>



<p>①と比較すると、東京リージョン以外ではSNSトピックが無くなり、東京リージョンではカスタムイベントバスとフィルタリングルールが増えています。</p>



<p>つまり各リージョンでサブスクライブする必要が無くなった一方で、東京リージョンでは検出結果を集約するカスタムイベントバスとフィルタリング&amp;整形用のルールを作成する必要が生じているということです。</p>



<p>まとめると&#8230;</p>



<p><strong><span class="marker-under-blue">メリット</span></strong></p>



<ul class="wp-block-list">
<li>東京リージョン以外でSNSトピックを作成する必要がない</li>



<li>フィルタリング&amp;整形用の同一設定を入れなくて済む</li>
</ul>



<p><strong><span class="marker-under-red">デメリット</span></strong></p>



<ul class="wp-block-list">
<li>構築難易度は低くはない(①と比較してですが)</li>
</ul>



<h3 class="wp-block-heading"><span id="toc5">③ AWS Security Hubのリージョン集約を有効化する</span></h3>



<p>3つ目の選択肢は、AWS Security Hubでのクロスリージョン集約機能を有効化し、各リージョンの検出結果をメインの東京リージョン(=ホームリージョン)に集めるというものです。</p>



<p>構成図は以下のようになります。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="589" src="https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-01-1024x589.png" alt="" class="wp-image-262" srcset="https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-01-1024x589.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-01-300x173.png 300w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-01-768x442.png 768w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-01-120x68.png 120w, https://tech-itoya.com/wp-content/uploads/2024/12/danto-20241206-01.png 1171w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Security Hubを使うことで東京リージョン以外にEventBridgeのリソースを作る必要がない点が、これまでの構成とは異なっています。</p>



<p>この場合だと、各リージョンの検出結果を(東京リージョンの)SecurityHubコンソール上から一挙に確認できるという視認性の良さはメリットになるはずです。</p>



<p>一方で、全リージョンでSecurity Hubを有効化しなくてはならないという点はデメリットになるかもしれません。</p>



<p>(これについては、セキュリティ要件として全リージョンですでにSecurity Hubを有効化していた場合はデメリットにはならないでしょう。)</p>



<p>まとめると&#8230;</p>



<p><strong><span class="marker-under-blue">メリット</span></strong></p>



<ul class="wp-block-list">
<li>構築難易度は低い(設定を有効化するだけ)</li>



<li>検出結果の視認性が良い</li>
</ul>



<p><strong><span class="marker-under-red">デメリット</span></strong></p>



<ul class="wp-block-list">
<li>全リージョンでSecurity Hubを有効化する必要がある</li>
</ul>



<p></p>



<p></p>



<p>以上で、選択肢の比較考量は終わりです。</p>



<p>実装については、また別の機会に記事にしたいと思います。</p>



<p></p>



<h2 class="wp-block-heading"><span id="toc6">2. 参考記事</span></h2>



<ul class="wp-block-list">
<li><a href="https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/finding-aggregation.html">Security Hub でのクロスリージョン集約について &#8211; AWS Security Hub</a>
<ul class="wp-block-list">
<li>クロスリージョン集約に関するAWS公式ドキュメント</li>
</ul>
</li>
</ul>



<ul class="wp-block-list">
<li><a href="https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-event-bus.html">Amazon のイベントバス EventBridge &#8211; Amazon EventBridge</a>
<ul class="wp-block-list">
<li>EventBridgeのイベントバスに関するAWS公式ドキュメント</li>
</ul>
</li>
</ul>



<ul class="wp-block-list">
<li><a href="https://dev.classmethod.jp/articles/guarduty-finding-aggregated-by-eventbridge/">GuardDuty 全リージョン分の検出結果を EventBridge で集約してからメール通知する CloudFormation テンプレートの紹介 | DevelopersIO</a>
<ul class="wp-block-list">
<li>EventBridgeからSNSで管理者に通知する仕掛けを紹介しているクラメソ記事</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="241"
					data-ulike-nonce="07f9c8a114"
					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_241"></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/%e5%90%84%e3%83%aa%e3%83%bc%e3%82%b8%e3%83%a7%e3%83%b3%e3%81%a7%e6%9c%89%e5%8a%b9%e5%8c%96%e3%81%97%e3%81%9fguardduty%e3%81%ae%e6%a4%9c%e5%87%ba%e7%b5%90%e6%9e%9c%e3%82%92%e7%ae%a1%e7%90%86%e8%80%85/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<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-8" checked><label class="toc-title" for="toc-checkbox-8">目次</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 loading="lazy" 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 loading="lazy" 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 loading="lazy" 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="cda39e5760"
					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>
		<item>
		<title>ALB + S3 + Lambda で画像やデザイン含むちょっとリッチなメンテナンス画面をお手軽実装で表示させる</title>
		<link>https://tech-itoya.com/alb-s3-lambda-%e3%81%a7%e7%94%bb%e5%83%8f%e3%82%84%e3%83%87%e3%82%b6%e3%82%a4%e3%83%b3%e5%90%ab%e3%82%80%e3%81%a1%e3%82%87%e3%81%a3%e3%81%a8%e3%83%aa%e3%83%83%e3%83%81%e3%81%aa%e3%83%a1%e3%83%b3/</link>
					<comments>https://tech-itoya.com/alb-s3-lambda-%e3%81%a7%e7%94%bb%e5%83%8f%e3%82%84%e3%83%87%e3%82%b6%e3%82%a4%e3%83%b3%e5%90%ab%e3%82%80%e3%81%a1%e3%82%87%e3%81%a3%e3%81%a8%e3%83%aa%e3%83%83%e3%83%81%e3%81%aa%e3%83%a1%e3%83%b3/#respond</comments>
		
		<dc:creator><![CDATA[tori-dash]]></dc:creator>
		<pubDate>Sun, 06 Oct 2024 02:51:53 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Lambda]]></category>
		<category><![CDATA[S3]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=147</guid>

					<description><![CDATA[こんにちは、とりです CloudFront や WAF が既に立っている構成では、そちらでメンテ画面を制御する方法がメジャーかと思いますが、そうでない場合 ALB で制御するケースもあるかと思います。 ALB で制御する [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんにちは、とりです</p>



<p>CloudFront や WAF が既に立っている構成では、そちらでメンテ画面を制御する方法がメジャーかと思いますが、そうでない場合 ALB で制御するケースもあるかと思います。</p>



<p>ALB で制御する方法もいろいろアイデアはあるかと思いますが、今回は lambda + S3 のお手軽実装でメンテページを表示させる方法について紹介したいと思います。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-10" checked><label class="toc-title" for="toc-checkbox-10">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">構成図</a></li><li><a href="#toc2" tabindex="0">Lambda の実装</a><ol><li><a href="#toc3" tabindex="0">Lambda コード</a></li></ol></li><li><a href="#toc4" tabindex="0">その他リスナールールやS3の作成</a></li><li><a href="#toc5" tabindex="0">おまけ: メンテナンス画面の表示方法いろいろ</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">構成図</span></h2>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="659" height="260" src="https://tech-itoya.com/wp-content/uploads/2024/10/tori-20241005-1.png" alt="" class="wp-image-150" srcset="https://tech-itoya.com/wp-content/uploads/2024/10/tori-20241005-1.png 659w, https://tech-itoya.com/wp-content/uploads/2024/10/tori-20241005-1-300x118.png 300w" sizes="(max-width: 659px) 100vw, 659px" /></figure>



<p>ALB のリスナーに メンテナンス用のLambdaを向いたルールを追加します。</p>



<p>普段は通常時用のリスナールールより優先順位を下げておき、メンテイン時は優先順位を上げることでメンテナンス画面を表示させます</p>



<p>ALB の操作だけでぱっとメンテインできるので、運用が楽ちんで嬉しいですね</p>



<h2 class="wp-block-heading"><span id="toc2">Lambda の実装</span></h2>



<h3 class="wp-block-heading"><span id="toc3">Lambda コード</span></h3>



<p>メインとなる lambda ロジックはこんな感じで実装をしました</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-lang="Python"><code>import boto3
import os
import base64

# 環境変数からS3の情報を取得
BUCKET_NAME = os.environ[&#39;BUCKET_NAME&#39;]

s3 = boto3.client(&#39;s3&#39;)

def lambda_handler(event, context):
    try:
        # リクエストされたパスからファイル名を取得
        file_name = event.get(&#39;path&#39;, &#39;&#39;).lstrip(&#39;/&#39;)
        if not file_name:
            file_name = &#39;maintenance.html&#39;  # デフォルトのファイル名

        # S3バケットからファイルを取得
        s3response = s3.get_object(Bucket=BUCKET_NAME, Key=file_name)
        body = s3response[&#39;Body&#39;].read()
        # S3オブジェクトのメタデータからContent-Typeを取得
        content_type = s3response[&#39;ContentType&#39;]

        # content_type が text 以外の場合は base64 エンコードを挟んでから utf-8 でデコード。
        if &#39;text&#39; in content_type:
            response_body = body.decode(&#39;utf-8&#39;)
            is_base64_encoded = False
        else:
            response_body = base64.b64encode(body).decode(&#39;utf-8&#39;)
            is_base64_encoded = True

        return {
            # メンテ画面のため 正常系だが 503 を返す
            &#39;statusCode&#39;: 503,
            &#39;headers&#39;: {
                &#39;Content-Type&#39;: content_type
            },
            &#39;body&#39;: response_body,
            &#39;isBase64Encoded&#39;: is_base64_encoded
        }
    except Exception as e:
        return {
            &#39;statusCode&#39;: 500,
            &#39;body&#39;: f&quot;Error getting the file from S3: {str(e)}&quot;
        }</code></pre></div>



<p>実装のポイントとしては以下となります</p>



<ul class="wp-block-list">
<li><code>event.get('path', '').lstrip('/')</code>で HTTP リクエストからパス部分のみ取り出して、S3のファイルパスとして使用しています</li>
</ul>



<ul class="wp-block-list">
<li>content_type は S3 ファイルのメタタグから Content-type を取得しています
<ul class="wp-block-list">
<li>小ネタですが、Content-type を何も指定せずにS3にファイルアップロードした場合は python 製のライブラリによって、そのファイルの型が自動で推測されるようです(<a href="https://github.com/aws/aws-cli/blob/833716e1dae531fe8c3835c5feaa50d255c01be1/awscli/customizations/s3/utils.py#L288">aws cli</a>)</li>
</ul>
</li>



<li>text は utf-8 でそのままデコードが可能ですが、画像ファイルなどは 一度 base64 でエンコードしてから utf-8 に直すとうまく表示されました</li>
</ul>



<h2 class="wp-block-heading"><span id="toc4">その他リスナールールやS3の作成</span></h2>



<p>Lamda を作った後は上記構成図の S3 や ALB のリスナールールを作成します</p>



<p>そのあたりの作業は特に特徴はないので、参考になる記事の紹介に留めたいと思います</p>





<a rel="noopener" href="https://dev.classmethod.jp/articles/alb-listener-rule-lambda-maintenance-page" title="ALBリスナールールとLambdaだけでメンテナンスページへの切替が簡単にできる仕組みを作る | DevelopersIO" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://devio2023-media.developers.io/wp-content/uploads/2019/05/aws-lambda.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ALBリスナールールとLambdaだけでメンテナンスページへの切替が簡単にできる仕組みを作る | DevelopersIO</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://dev.classmethod.jp/articles/alb-listener-rule-lambda-maintenance-page/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">dev.classmethod.jp</div></div></div></div></a>






<a rel="noopener" href="https://qiita.com/kizashi/items/1156e81393dcfb81a5eb" title="ALBで1025文字以上のメンテナンス画面を表示する方法 - Qiita" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-user-contents.imgix.net%2Fhttps%253A%252F%252Fcdn.qiita.com%252Fassets%252Fpublic%252Farticle-ogp-background-afbab5eb44e0b055cce1258705637a91.png%3Fixlib%3Drb-4.0.0%26w%3D1200%26blend64%3DaHR0cHM6Ly9xaWl0YS11c2VyLXByb2ZpbGUtaW1hZ2VzLmltZ2l4Lm5ldC9odHRwcyUzQSUyRiUyRnMzLWFwLW5vcnRoZWFzdC0xLmFtYXpvbmF3cy5jb20lMkZxaWl0YS1pbWFnZS1zdG9yZSUyRjAlMkY5MjMxMTQlMkYxNWQ1OTIwNWQ1MmQ2NjhmYjJmZjdhYTVmMjkwYzNhMThhNTAzNDY0JTJGbGFyZ2UucG5nJTNGMTYxMDA3OTUwMT9peGxpYj1yYi00LjAuMCZhcj0xJTNBMSZmaXQ9Y3JvcCZtYXNrPWVsbGlwc2UmYmc9RkZGRkZGJmZtPXBuZzMyJnM9NTA2MTk4YjA0MDdkMDJkY2Q1OTJmZTMyNmE5NTQyNjE%26blend-x%3D120%26blend-y%3D467%26blend-w%3D82%26blend-h%3D82%26blend-mode%3Dnormal%26s%3Db2192c45778d99f3efd88c6a65dc3d85?ixlib=rb-4.0.0&#038;w=1200&#038;fm=jpg&#038;mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk2MCZoPTMyNCZ0eHQ9QUxCJUUzJTgxJUE3MTAyNSVFNiU5NiU4NyVFNSVBRCU5NyVFNCVCQiVBNSVFNCVCOCU4QSVFMyU4MSVBRSVFMyU4MyVBMSVFMyU4MyVCMyVFMyU4MyU4NiVFMyU4MyU4QSVFMyU4MyVCMyVFMyU4MiVCOSVFNyU5NCVCQiVFOSU5RCVBMiVFMyU4MiU5MiVFOCVBMSVBOCVFNyVBNCVCQSVFMyU4MSU5OSVFMyU4MiU4QiVFNiU5NiVCOSVFNiVCMyU5NSZ0eHQtYWxpZ249bGVmdCUyQ3RvcCZ0eHQtY29sb3I9JTIzMUUyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTU2JnR4dC1wYWQ9MCZzPTE1YzZjZmUzNTMzZmQxNDcwYWU0ZjI5MzZjODY1ZmU1&#038;mark-x=120&#038;mark-y=112&#038;blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTgzOCZoPTU4JnR4dD0lNDBraXphc2hpJnR4dC1jb2xvcj0lMjMxRTIxMjEmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9MzYmdHh0LXBhZD0wJnM9OTI2MjBjOTk4MmM5YTJiYzE4NGFjOWUzNWIwZTdlZDI&#038;blend-x=242&#038;blend-y=480&#038;blend-w=838&#038;blend-h=46&#038;blend-fit=crop&#038;blend-crop=left%2Cbottom&#038;blend-mode=normal&#038;s=f66aa48a50b9a6f81a9a41d77defdc63" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ALBで1025文字以上のメンテナンス画面を表示する方法 - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">背景 AWSのALB（Application Load Balancer）のリスナールールを使ってメンテナンス画面を表示することは一般的ですが、ALBのリスナールールには1024文字の制限があります。 そのため、1024文字を超えるメンテナ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://qiita.com/kizashi/items/1156e81393dcfb81a5eb" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">qiita.com</div></div></div></div></a>




<p>上記の記事の手順に加えて、 Lambda -&gt; S3 の間に、Gateway 型の VPC エンドポイントを噛ませておけると、S3 のアウトバウンドの転送料金がかからなくなるため、ある程度のトラフィック量が見込まれる場合は入れておくといいかもしれません。</p>





<a rel="noopener" href="https://repost.aws/ja/questions/QU71ng4VdySYaZa8B44TrVJA/ec2-outbound-data-transfer-cost-to-s3-gateway-endpoint?sc_ichannel=ha&#038;sc_ilang=en&#038;sc_isite=repost&#038;sc_iplace=hp&#038;sc_icontent=QU71ng4VdySYaZa8B44TrVJA&#038;sc_ipos=13" title="ERROR: The request could not be satisfied" class="blogcard-wrap external-blogcard-wrap a-wrap cf" target="_blank"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img loading="lazy" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Frepost.aws%2Fja%2Fquestions%2FQU71ng4VdySYaZa8B44TrVJA%2Fec2-outbound-data-transfer-cost-to-s3-gateway-endpoint%3Fsc_ichannel%3Dha%26sc_ilang%3Den%26sc_isite%3Drepost%26sc_iplace%3Dhp%26sc_icontent%3DQU71ng4VdySYaZa8B44TrVJA%26sc_ipos%3D13?w=160&#038;h=90" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="160" height="90" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">ERROR: The request could not be satisfied</div><div class="blogcard-snippet external-blogcard-snippet"></div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img loading="lazy" decoding="async" src="https://www.google.com/s2/favicons?domain=https://repost.aws/ja/questions/QU71ng4VdySYaZa8B44TrVJA/ec2-outbound-data-transfer-cost-to-s3-gateway-endpoint?sc_ichannel=ha&#038;sc_ilang=en&#038;sc_isite=repost&#038;sc_iplace=hp&#038;sc_icontent=QU71ng4VdySYaZa8B44TrVJA&#038;sc_ipos=13" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">repost.aws</div></div></div></div></a>




<h2 class="wp-block-heading"><span id="toc5">おまけ: メンテナンス画面の表示方法いろいろ</span></h2>



<p>冒頭で軽く触れた通り、ALB でメンテナンス画面を表示させるにはいろいろな実装方法があります。</p>



<p>個人的に方法を比較した際のメモを残しておきます。</p>



<p>今回はサービスの性質上あまり大量アクセスを見込んでいなかったため、実装のお手軽さを重視して Lambda を採用してみました。トラフィックがある程度見込まれるサービスの場合は、Cloudfront 経由がなんだかんだ綺麗なのかもなという印象でした。</p>



<p><span class="marker-under">1.ALBの固定レスポンスで返す</span></p>



<ul class="wp-block-list">
<li>参考記事: <a href="https://www.netassist.ne.jp/techblog/29666/">https://www.netassist.ne.jp/techblog/29666/</a></li>



<li>ALB 単体で完結するため、実装としては一番楽だしコストも安い</li>



<li>別ファイルのCSSや画像の読み込みはできない</li>



<li>レスポンスの内容は1024文字までに制限されるのでリッチなページは返せない</li>
</ul>



<p><span class="marker-under">2.Cloudfrontを経由してメンテ画面を表示</span></p>



<ul class="wp-block-list">
<li>参考記事:<a href="https://techblog.nhn-techorus.com/archives/21488">https://techblog.nhn-techorus.com/archives/21488</a></li>



<li>AWS上で完結させる要件だとおそらく一番メジャーな実装方法</li>



<li>CDNキャッシュが効く分、早くレスポンスが返る嬉しさがある</li>



<li>CDNキャッシュされて、S3へのリクエスト量が節約できるので若干のコストメリットがある</li>



<li>この中だと実装が比較的重め (IaC してる場合だと設定値多いリソースなので特に)</li>
</ul>



<p><span class="marker-under">3.Privatelinkを経由してメンテ画面を表示</span></p>



<ul class="wp-block-list">
<li>参考記事:<a href="https://aws.amazon.com/jp/blogs/news/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/">https://aws.amazon.com/jp/blogs/news/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/</a></li>



<li>VPC エンドポイント追加して向けるだけなので実装はかなり楽</li>



<li>インターフェース型 VPC エンドポイントを使うので、コストは結構かかっちゃう</li>
</ul>



<p><span class="marker-under"><strong>4.Lambda を経由してメンテ画面を表示 (今回の方法)</strong></span></p>



<ul class="wp-block-list">
<li>参考記事: <a href="https://dev.classmethod.jp/articles/alb-listener-rule-lambda-maintenance-page/">https://dev.classmethod.jp/articles/alb-listener-rule-lambda-maintenance-page/</a></li>



<li>実装はまあまあお手軽</li>



<li>lambda ロジックの管理コストがかかる
<ul class="wp-block-list">
<li>とはいえ layer も不要で、大分軽微な実装なので運用負荷はそんな高くないはず</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="147"
					data-ulike-nonce="ea315832d1"
					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_147"></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/alb-s3-lambda-%e3%81%a7%e7%94%bb%e5%83%8f%e3%82%84%e3%83%87%e3%82%b6%e3%82%a4%e3%83%b3%e5%90%ab%e3%82%80%e3%81%a1%e3%82%87%e3%81%a3%e3%81%a8%e3%83%aa%e3%83%83%e3%83%81%e3%81%aa%e3%83%a1%e3%83%b3/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Terragrunt + Tfcmt + Github Actions で Plan 結果の Summary を Github 上に表示したい</title>
		<link>https://tech-itoya.com/terragrunt-tfcmt-github-actions-%e3%81%a7-plan-%e7%b5%90%e6%9e%9c%e3%81%ae-summary-%e3%82%92-github-%e4%b8%8a%e3%81%ab%e8%a1%a8%e7%a4%ba%e3%81%97%e3%81%9f%e3%81%84/</link>
					<comments>https://tech-itoya.com/terragrunt-tfcmt-github-actions-%e3%81%a7-plan-%e7%b5%90%e6%9e%9c%e3%81%ae-summary-%e3%82%92-github-%e4%b8%8a%e3%81%ab%e8%a1%a8%e7%a4%ba%e3%81%97%e3%81%9f%e3%81%84/#respond</comments>
		
		<dc:creator><![CDATA[tori-dash]]></dc:creator>
		<pubDate>Thu, 19 Sep 2024 17:09:51 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Github Actions]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[Terraform]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=32</guid>

					<description><![CDATA[こんにちは、とりです。 皆さんは Terraform リポジトリを管理する際、CIを組んでいるでしょうか？ プルリク公開時に Terraform Plan を自動実行して、結果をGithub上で確認できると、変更箇所が伝 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんにちは、とりです。</p>



<p>皆さんは Terraform リポジトリを管理する際、CIを組んでいるでしょうか？</p>



<p>プルリク公開時に Terraform Plan を自動実行して、結果をGithub上で確認できると、変更箇所が伝わりやすくなりハッピーかと思います。</p>



<p>tfcmt はそんなTerraformのCIを組みたいときに活躍するツールで、terraform plan の結果を素敵な感じに整形して、プルリクにコメントしてくれます</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="909" height="485" data-id="121" src="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-1.png" alt="" class="wp-image-121" srcset="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-1.png 909w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-1-300x160.png 300w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-1-768x410.png 768w" sizes="(max-width: 909px) 100vw, 909px" /></figure>
</figure>



<p>ただ、Terragrunt のような複数の Statefile を管理するリポジトリの場合、プルリク上に大量にコメントが流れてしまい、かえって見づらくなることもあるかなと思います。</p>



<p>今回触っていた IaC リポジトリでも、6環境×モジュールが6~7個あったため、最大40個強のPlan結果がプルリクコメントに並んでしまうことになり、さすがにこれを運用すると辛そうだな&#8230;という印象でした。</p>



<p>↓ は Bot が PR に大量にコメントを流している様子です</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="744" height="885" src="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-2.png" alt="bot-tfcmt" class="wp-image-122" style="width:840px;height:auto" srcset="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-2.png 744w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-2-252x300.png 252w" sizes="(max-width: 744px) 100vw, 744px" /></figure>



<p>そこで、Github Workflow の Summary に結果を見やすくまとめることで、上記の課題を解決しつつ、快適に terraform の CI を運用する方法を紹介したいと思います。</p>



<p>Terragrunt でなく生の Terraform を使っていても、Module 等で State の分割管理を行っている場合、同様の課題感を持っていたりすると思うので、そういった方の参考にもなれば幸いです。</p>



<p>ついでに Github Enterprise 環境で tfcmt を構築している記事もあまり見かけなかったので、その解説もできたらと思っています。</p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-12" checked><label class="toc-title" for="toc-checkbox-12">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">結論</a></li><li><a href="#toc2" tabindex="0">実装</a><ol><li><a href="#toc3" tabindex="0">全体のディレクトリ構成</a></li><li><a href="#toc4" tabindex="0">tfcmt.yml の設定</a></li><li><a href="#toc5" tabindex="0">tfwrapper.sh の設定</a></li><li><a href="#toc6" tabindex="0">Github Actions のワークフロー</a></li></ol></li><li><a href="#toc7" tabindex="0">改善ポイント</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">結論</span></h2>



<p>いろいろ試した結果、こんな形で落ち着きました。</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="489" data-id="124" src="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1024x489.png" alt="" class="wp-image-124" srcset="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1024x489.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-300x143.png 300w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-768x366.png 768w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1536x733.png 1536w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3.png 1878w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p>環境ごとに Github Actions のワークフローを作成し、サマリー部分にモジュールごと実行した Plan 結果を見やすくまとめています。</p>



<p>逆にプルリクのラベル付けやコメントを書き込む機能などは、明示的に無効化を行いました。</p>



<h2 class="wp-block-heading"><span id="toc2">実装</span></h2>



<h3 class="wp-block-heading"><span id="toc3">全体のディレクトリ構成</span></h3>



<p>Terraform リポジトリのディレクトリ構成としてはこんな感じになっています</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>.
├── .github
│   ├── workflows
│   │   ├── develop_terraform_plan.yml
│   │   ├── staging_terraform_plan.yml
│   │   └── ... (other files)
│   └── tfcmt
│       ├── tfcmt.yml
│       └── tfwrapper.sh
├── environments
│   ├── develop
│   │   └── ap-northeast-1
│   │       └── ... (modules)
│   └── ... (other environments)
├── modules
│   ├── ecs
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── variables.tf
│   │   └── ... (other files)
│   └── ... (other modules)
└── terragrunt.hcl</code></pre></div>



<h3 class="wp-block-heading"><span id="toc4">tfcmt.yml の設定</span></h3>



<p>tfcmt.yml という名前で config ファイルを書いておき、tfcmt コマンド実行時にオプションに指定してあげることで、plan 結果を整形するフォーマットをカスタマイズできます。</p>



<p>かなりカスタマイズしやすく、個人的に感動したポイントでした。</p>



<p>参考: <a href="https://suzuki-shunsuke.github.io/tfcmt/config#default-configuration">https://suzuki-shunsuke.github.io/tfcmt/config#default-configuration</a></p>



<p>今回設定した tfcmt.yml の内容になります</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>embedded_var_names: []
terraform:
  plan:
    # github enterprise の時のみ設定が必要
    # 参考: https://suzuki-shunsuke.github.io/tfcmt/github-enterprise
    ghe_base_url: https://XXX.github.com
    ghe_graphql_endpoint: https://XXX.github.com/api/graphql
    # terragrunt だと statefile 毎ラベルが上書きされて逆に管理し辛かったので無効化
    disable_label: true
    template: |
      &lt;details&gt;&lt;summary&gt;&lt;strong&gt; :package: Target Module: {{.Vars.target}} &lt;/strong&gt;&lt;/summary&gt;
        {{template &quot;result&quot; .}}
        {{template &quot;updated_resources&quot; .}}
        {{template &quot;changed_result&quot; .}}
        {{template &quot;change_outside_terraform&quot; .}}
        {{template &quot;warning&quot; .}}
        {{template &quot;error_messages&quot; .}}
      &lt;/details&gt;</code></pre></div>



<p>ポイントとしては以下になります</p>



<ul class="wp-block-list">
<li>Github Enterprise の場合は plan 実行時の 環境変数に <code>ghe_base_url</code> と <code>ghe_graphql_endpoint</code> を足してあげる必要があります。</li>



<li>tfcmt は自動で plan 結果を要約するようなラベル付けを行ってくれるのですが、複数の Statefile を管理するリポジトリの場合は、逆に見辛さが勝ってしまったので無効化しています。</li>



<li><strong>template:</strong> に続くブロックで plan 結果をどんな感じで整形するか指定できるので、お好みでいじってみても良いかと思います。</li>
</ul>



<h3 class="wp-block-heading" id="tfwrapper.sh"><span id="toc5">tfwrapper.sh の設定</span></h3>



<p>Terragrunt の場合、tfcmt が <code>terragrunt run-all plan</code> コマンドをそのまま叩けないので wrapper のスクリプトをかませてあげる必要があります。</p>



<p>Terraform をそのまま使っている場合はこの作業は不要かなと思います。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>#!/bin/bash
set -euo pipefail

# コマンドの種類を取得(例: apply, plan, fmt...)
type=$(echo &quot;$@&quot; |  awk &#39;{print $1}&#39;)

# カレントディレクトリを整形して、target の module 名を取得
base_dir=$(git rev-parse --show-toplevel)
target=${PWD#&quot;$base_dir&quot;/}
target=$(echo &quot;$target&quot; | sed &#39;s|/\.terragrunt-cache/.*||&#39;)

if [ &quot;$type&quot; == &quot;plan&quot; ]; then
    tfcmt --config &quot;$GITHUB_WORKSPACE/.github/tfcmt/tfcmt.yml&quot; \
          --output &quot;/tmp/tgplan.md&quot; \
          -var &quot;target:${target}&quot; \
          plan -patch -- terraform &quot;$@&quot;
fi</code></pre></div>



<p>ここが terragrunt run-all plan コマンドを実行した時の実体のスクリプトになるのですが、その際に　tfcmt の処理を噛ませてあげる流れとなっています</p>



<p>以下オプションの説明になります</p>



<ul class="wp-block-list">
<li><strong>&#8211;config オプション</strong>: plan 結果の整形フォーマットなどを config ファイルに書くことができます。上のステップで作成した tfcmt.yml ファイルを config として指定します</li>
</ul>



<ul class="wp-block-list">
<li><strong>&#8211;output オプション:</strong> tfcmt のメインの使い方は、プルリクのコメントに plan 結果を出力することだと思いますが、今回はそれを無効化しローカルにファイルに吐き出しています</li>
</ul>



<ul class="wp-block-list">
<li><strong>-var オプション:</strong> tfcmt に変数を渡すことができます。今回は terraform のモジュール名を渡して、tfcmt が Plan 結果を整形する際に、モジュール名も含めて出力できるようにしています</li>
</ul>



<p>細かい仕様は 公式 doc を参照ください。</p>



<p>参考: <a href="https://suzuki-shunsuke.github.io/tfcmt/terragrunt-run-all">https://suzuki-shunsuke.github.io/tfcmt/terragrunt-run-all</a></p>



<h3 class="wp-block-heading"><span id="toc6">Github Actions のワークフロー</span></h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>name: Execute terragrunt plan
on:
  pull_request:
    branches:
      - &#39;main&#39;
    paths:
      - &#39;environments/develop/**&#39;
      - &#39;modules/**&#39;
  workflow_dispatch:
env:
  Enviromnet: develop
  IAM_ROLE_ARN: XXX
jobs:
  plan-common:
    runs-on: XXX
    steps:
      - uses: actions/checkout@v3

     # plan 用の権限を assume する
      - name: assume ECS Role
 　　 　uses: aws-actions/configure-aws-credentials@v4
   　　　 with:
   　　　   aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
      　　　aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      　　　aws-region: ap-northeast-1

      # tfcmt のインストール
      - name: setup tfcmt
        env:
          TFCMT_VERSION: v4.13.0
        run: |
          curl -L &quot;https://github.com/suzuki-shunsuke/tfcmt/releases/download/${TFCMT_VERSION}/tfcmt_linux_amd64.tar.gz&quot; -o /tmp/tfcmt.tar.gz
          tar xzf /tmp/tfcmt.tar.gz -C /tmp
          mv /tmp/tfcmt /usr/local/bin
          tfcmt --version

      # terragrunt plan の実行
      - name: Excute terragrunt plan
        shell: bash
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          cd ./environments/${{ env.Enviromnet }}
          terragrunt run-all init
          chmod a+x $GITHUB_WORKSPACE/.github/tfcmt/tfwrapper.sh
          terragrunt run-all plan -no-color -input=false --terragrunt-tfpath $GITHUB_WORKSPACE/.github/tfcmt/tfwrapper.sh

      # actions の workflow summary に plan 結果の summary を通知する
      - name: Create Job Summary
        if: always()
        uses: actions/github-script@v6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const fs = require(&#39;fs&#39;);
            const filePath = &#39;/tmp/tgplan.md&#39;;
            const output = fs.readFileSync(filePath, &#39;utf8&#39;);
            await core.summary
              .addHeading(&#39;Terragrunt plan report&#39;)
              .addRaw(output)
              .write();</code></pre></div>



<p>まず terragrunt plan を実行するための IAM 権限の Assume Role と、tfcmt をローカルにインストール処理を実行後、メインとなる terragrunt run-all plan を実行しています。</p>



<p><a href="#tfwrapper.sh">ここで補足した通り</a>、tfcmt が直接 terragrunt run-all plan を実行することができないので、<code>--terragrunt-tfpath</code> オプションで wrapper スクリプトを呼び出し、そのスクリプト内で tfcmt を実行させています。</p>



<p>その後、<code>actions/github-script@v6</code> を使い、tfcmt が吐き出した md ファイルを Github の Summary 部分に書き込む処理を入れています。</p>



<p>結果、こんな感じで Summary に環境ごとの Plan 結果をまとめることができました！</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="489" src="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1-1024x489.png" alt="" class="wp-image-125" srcset="https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1-1024x489.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1-300x143.png 300w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1-768x366.png 768w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1-1536x733.png 1536w, https://tech-itoya.com/wp-content/uploads/2024/09/tori-20240920-3-1.png 1878w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading"><span id="toc7">改善ポイント</span></h2>



<p>使い勝手自体の改善は満足したので、このまま他のいろんなIaCリポジトリに横展開していきたいと考えています。</p>



<p>横展開する際、毎回 tfcmt の Config やらを設定するのは面倒なので、共通部品化して Actions モジュールを他の IaC リポジトリに配布できるといいなと考えています。機会があればチャレンジしてみようと思います。</p>



<p>tfcmt のおかげで Terraform の CI 体験が大分改善しそうで楽しみです！</p>



<p></p>
		<div class="wpulike wpulike-heart " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="いいねボタン"
					data-ulike-id="32"
					data-ulike-nonce="7f6451ed9f"
					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_32"></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/terragrunt-tfcmt-github-actions-%e3%81%a7-plan-%e7%b5%90%e6%9e%9c%e3%81%ae-summary-%e3%82%92-github-%e4%b8%8a%e3%81%ab%e8%a1%a8%e7%a4%ba%e3%81%97%e3%81%9f%e3%81%84/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Amazon QuickSightとMicrosoft Entra IDをSAML連携させた場合のSessionDurationの有効範囲について</title>
		<link>https://tech-itoya.com/amazon-quicksight%e3%81%a8microsoft-entra-id%e3%82%92saml%e9%80%a3%e6%90%ba%e3%81%95%e3%81%9b%e3%81%9f%e5%a0%b4%e5%90%88%e3%81%aesessionduration%e3%81%ae%e5%bd%b9%e5%89%b2/</link>
					<comments>https://tech-itoya.com/amazon-quicksight%e3%81%a8microsoft-entra-id%e3%82%92saml%e9%80%a3%e6%90%ba%e3%81%95%e3%81%9b%e3%81%9f%e5%a0%b4%e5%90%88%e3%81%aesessionduration%e3%81%ae%e5%bd%b9%e5%89%b2/#respond</comments>
		
		<dc:creator><![CDATA[アーサー・C・ダントー]]></dc:creator>
		<pubDate>Wed, 18 Sep 2024 16:07:25 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[QuickSight]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=30</guid>

					<description><![CDATA[こんにちは。Dantoです。 今回は、Amazon QuickSightとMicrosoft Entra IDをSAML連携した場合のSessionDuration値の役割について書いていきます。 目次 TL; DR;本 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんにちは。Dantoです。</p>



<p>今回は、Amazon QuickSightとMicrosoft Entra IDをSAML連携した場合のSessionDuration値の役割について書いていきます。</p>



<p></p>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-14" checked><label class="toc-title" for="toc-checkbox-14">目次</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">本題</a><ol><li><a href="#toc3" tabindex="0">前提</a></li><li><a href="#toc4" tabindex="0">疑問</a></li><li><a href="#toc5" tabindex="0">結論</a></li></ol></li><li><a href="#toc6" tabindex="0">おまけ</a></li><li><a href="#toc7" tabindex="0">参考記事</a></li></ol>
    </div>
  </div>

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



<ul class="wp-block-list">
<li>SessionDurationの設定値はQuickSightのセッション継続時間とは無関係
<ul class="wp-block-list">
<li>QuickSightのセッション継続時間は独自管理となっており、12時間固定</li>
</ul>
</li>
</ul>



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



<h3 class="wp-block-heading"><span id="toc3">前提</span></h3>



<p>Amazon QuickSightとMicrosoft Entra IDをSAML連携させた場合、オプションでEntra ID側にSessionDuration値を設定することができます。</p>



<p>SessionDurationとは、読んで字のごとく「セッションを継続させる時間」を表す属性ですが、念のため公式ドキュメントの記述を引用しておきます。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><span class="fz-12px"><span class="fz-14px">(Optional) You can use an&nbsp;<code>Attribute</code>&nbsp;element with the&nbsp;<code>Name</code>&nbsp;attribute set to&nbsp;<code>https://aws.amazon.com/SAML/Attributes/SessionDuration"</code>. This element contains one&nbsp;<code>AttributeValue</code>&nbsp;element that specifies how long the user can access the AWS Management Console before having to request new temporary credentials. The value is an integer representing the number of seconds for the session. The value can range from 900 seconds (15 minutes) to 43200 seconds (12 hours). If this attribute is not present, then the credential last for one hour (the default value of the&nbsp;<code>DurationSeconds</code>&nbsp;parameter of the&nbsp;<code>AssumeRoleWithSAML</code>&nbsp;API).</span></span></p>



<p><span class="fz-14px">(オプション) Name 属性を https://aws.amazon.com/SAML/Attributes/SessionDuration&#8221; に設定した Attribute 要素を使用できます。この要素には AttributeValue 要素が 1 つ含まれ、ユーザーが新しい一時的な認証情報を要求するまでに AWS Management Console にアクセスできる時間を指定します。値は、セッションの秒数を表す整数です。値の範囲は 900 秒（15 分）から 43200 秒（12 時間）です。この属性が存在しない場合は、クレデンシャルは 1 時間持続します (AssumeRoleWithSAML API の DurationSeconds パラメータのデフォルト値)。</span></p>
</blockquote>



<p></p>



<p>ドキュメントからは３つの情報を読み取ることができそうです。</p>



<ul class="wp-block-list">
<li>https://aws.amazon.com/SAML/Attributes/SessionDurationの値として<strong>整数値を設定</strong>できる</li>



<li>SessionDurationでは<strong>AWSマネージメントコンソールにアクセスできる時間を指定</strong>する</li>



<li><strong>設定値の範囲は900(15分)から43200(12時間)</strong>
<ul class="wp-block-list">
<li>デフォルトは3600(1時間)</li>
</ul>
</li>
</ul>



<p></p>



<p>ひとまず、SessionDurationに設定した値はAWSマネージメントコンソールにアクセス可能な時間と関係していることは理解しました。</p>



<p></p>



<p>また、ここではIDプロバイダにIAMロールを割り当てる際に、許可されるアクセスとして「プログラムと AWS マネジメントコンソールへのアクセスを許可する」を選択しています。</p>



<p></p>



<h3 class="wp-block-heading"><span id="toc4">疑問</span></h3>



<p><strong>SessionDurationの値は、QuickSightコンソールにアクセスできるセッションの継続時間とは関係ないのか…？</strong></p>



<p>(SessionDurationを900秒に設定したらQuickSightのセッション継続時間も900秒になるのか…？)</p>



<p></p>



<p>(↓↓↓QuickSightコンソール)</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="515" src="https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-1-1024x515.png" alt="" class="wp-image-127" srcset="https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-1-1024x515.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-1-300x151.png 300w, https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-1-768x386.png 768w, https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-1.png 1120w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading"><span id="toc5">結論</span></h3>



<p><strong>SessionDurationの設定値とQuickSight側のセッション継続時間は無関係</strong></p>



<p>あくまで、SessionDurationはAWSマネージメントコンソールにアクセス可能なセッションとのみ関係しているそうです。</p>



<p><strong>また、QuickSight側のセッション継続時間は独自管理であり、12時間固定で変更はできません。</strong></p>



<p></p>



<p>なので、SessionDurationを設定値を900とした場合のセッション継続時間は</p>



<ul class="wp-block-list">
<li><strong>AWSマネージメントコンソール : 15分</strong></li>



<li><strong>QuickSightコンソール : 12時間</strong></li>
</ul>



<p>となります。</p>



<p></p>



<h2 class="wp-block-heading"><span id="toc6">おまけ</span></h2>



<p>厄介なことに、IDフェデレーションによってQuickSightにサインインするユーザは、AWSコンソールにもサインインできてしまいます。</p>



<p>※AWSマネージメントコンソールへのサインインを防ぐ方法はありません。</p>



<p></p>



<p>ただし、引き受けさせるIAMロールの権限を絞っておけば、基本的には何もできない状態にはできます。</p>



<p></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-json" data-lang="JSON"><code>{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Sid&quot;: &quot;quicksight&quot;,
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Action&quot;: [
                &quot;quicksight:DescribeDashboard&quot;,
                &quot;quicksight:ListDashboards&quot;
            ],
            &quot;Resource&quot;: &quot;arn:aws:quicksight::123456789012:*&quot;
        }
    ]
}</code></pre></div>



<p></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="477" src="https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-2-1024x477.png" alt="" class="wp-image-128" srcset="https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-2-1024x477.png 1024w, https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-2-300x140.png 300w, https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-2-768x358.png 768w, https://tech-itoya.com/wp-content/uploads/2024/09/danto-20240919-2.png 1118w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p></p>



<p>AWSマネージメントコンソールへのアクセスを許容し難い場合は、せめてもの対策として</p>



<ul class="wp-block-list">
<li>IDプロバイダに割り当てるIAMロールの権限を最小限にする</li>



<li>SessionDurationの値を最小限にする</li>
</ul>



<p>この辺は講じてもよいかもしれません。</p>



<p></p>



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



<h2 class="wp-block-heading"><span id="toc7">参考記事</span></h2>



<ul class="wp-block-list">
<li><a href="https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_create_saml_assertions.html">認証レスポンス用の SAML アサーションを設定する &#8211; AWS Identity and Access Management (amazon.com)</a>
<ul class="wp-block-list">
<li>公式ドキュメント</li>
</ul>
</li>



<li><a href="https://dev.classmethod.jp/articles/tsnote-quicksight-login-01/">QuickSightへのアクセス時に「セッションで問題が発生しました。新しいセッションを開始するには、管理者から指定されたリンクに戻ってください。」とメッセージが表示される場合の対処方法 | DevelopersIO (classmethod.jp)</a>
<ul class="wp-block-list">
<li>QuickSightのセッションについてのクラスメソッドさん記事</li>
</ul>
</li>
</ul>



<p></p>
		<div class="wpulike wpulike-heart " ><div class="wp_ulike_general_class wp_ulike_is_restricted"><button type="button"
					aria-label="いいねボタン"
					data-ulike-id="30"
					data-ulike-nonce="683e0a2fb6"
					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_30"></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/amazon-quicksight%e3%81%a8microsoft-entra-id%e3%82%92saml%e9%80%a3%e6%90%ba%e3%81%95%e3%81%9b%e3%81%9f%e5%a0%b4%e5%90%88%e3%81%aesessionduration%e3%81%ae%e5%bd%b9%e5%89%b2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
