<?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>tori-dash | 伊東屋TECHブログ</title>
	<atom:link href="https://tech-itoya.com/author/tomoki-ono/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>tori-dash | 伊東屋TECHブログ</title>
	<link>https://tech-itoya.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>N8NのアカウントをEntraID経由でSSO管理してみた</title>
		<link>https://tech-itoya.com/n8n%e3%81%ae%e3%82%a2%e3%82%ab%e3%82%a6%e3%83%b3%e3%83%88%e3%82%92entraid%e7%b5%8c%e7%94%b1%e3%81%a7%e7%ae%a1%e7%90%86%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/</link>
					<comments>https://tech-itoya.com/n8n%e3%81%ae%e3%82%a2%e3%82%ab%e3%82%a6%e3%83%b3%e3%83%88%e3%82%92entraid%e7%b5%8c%e7%94%b1%e3%81%a7%e7%ae%a1%e7%90%86%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/#respond</comments>
		
		<dc:creator><![CDATA[tori-dash]]></dc:creator>
		<pubDate>Sun, 05 Oct 2025 01:14:46 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[EntraID]]></category>
		<category><![CDATA[N8N]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=350</guid>

					<description><![CDATA[前提条件 EntraID や N8N の管理者権限が作業時必要になります 設定 手順1: N8N 側で必要な情報を取得 手順2: EntraID でエンタープライズアプリケーションを作成 2-1. 新規アプリケーションの [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">前提条件</h2>



<p>EntraID や N8N の管理者権限が作業時必要になります</p>



<ul class="wp-block-list">
<li><strong><span class="marker-under">Entra ID のグローバル管理者権限、またはアプリケーション管理者権限</span></strong></li>



<li><strong><span class="marker-under">N8N のインスタンスオーナーまたは管理者権限</span></strong></li>



<li><strong><span class="marker-under">N8N の契約プランが Buisiness or Enterprise であること</span></strong></li>
</ul>



<h2 class="wp-block-heading">設定</h2>



<h3 class="wp-block-heading">手順1: N8N 側で必要な情報を取得</h3>



<ol class="wp-block-list">
<li><strong>N8N に管理者権限でログイン</strong></li>



<li><strong>Settings &gt; SSO に移動</strong></li>



<li><strong>以下の情報をメモ：</strong>
<ul class="wp-block-list">
<li><strong>ACS URL (Redirect URL)</strong>: <code>https://your-n8n-domain.com/rest/sso/saml/acs</code></li>



<li><strong>Entity ID</strong>: <code>https://your-n8n-domain.com/rest/sso/saml/metadata</code></li>
</ul>
</li>
</ol>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="444" src="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-1-1024x444.png" alt="" class="wp-image-351" srcset="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-1-1024x444.png 1024w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-1-300x130.png 300w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-1-768x333.png 768w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-1-1536x666.png 1536w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-1.png 1848w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">手順2: EntraID でエンタープライズアプリケーションを作成</h3>



<h4 class="wp-block-heading">2-1. 新規アプリケーションの作成</h4>



<ol class="wp-block-list">
<li><strong>Entra ID にサインイン</strong></li>



<li><strong>左トグルから [Entra ID] > [Enterprise applications] > [New application] をクリック</strong></li>



<li><strong>Create your own application をクリック</strong></li>



<li><strong>以下を入力して Create をクリック</strong>
<ul class="wp-block-list">
<li><strong>Name: n8n (任意)</strong></li>



<li><strong>選択: 「ギャラリーにない他のアプリケーションを統合する（Non-gallery）」</strong></li>
</ul>
</li>
</ol>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary><strong>💡TIPS: Gallery と Non-Gallery のアプリの違いについて</strong></summary>
<ul class="wp-block-list">
<li><strong>Gallery アプリ（ギャラリーアプリ）</strong>
<ul class="wp-block-list">
<li>Microsoft が事前に用意した、人気アプリとの統合テンプレート</li>



<li>SSO 連携等に必要な Identifier（Entity ID）や 必要なクレームなどが設定済み</li>



<li>Gallery で統合するアプリの例: Slack, Zoom, Salesforce, etc&#8230;</li>
</ul>
</li>



<li><strong>Non-Gallery アプリ（非ギャラリーアプリ）</strong>
<ul class="wp-block-list">
<li>ギャラリーにないアプリや、カスタム設定が必要なアプリを手動で統合する方法</li>



<li>Non-Gallery で統合するアプリの例: 社内開発アプリ, オンプレミスアプリ, ニッチな SaaS ツール&#8230;</li>
</ul>
</li>
</ul>
</details>
</blockquote>
</div>
</div>



<h4 class="wp-block-heading">2-2. SAML シングルサインオンの設定</h4>



<ol class="wp-block-list">
<li>作成したアプリケーションを開く</li>



<li>左メニューから Single sign-on を選択</li>



<li>SAML をクリックし、以下の項目を設定する</li>
</ol>



<div class="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
<ul class="wp-block-list">
<li><strong>基本的な SAML 構成</strong>
<ul class="wp-block-list">
<li>手順1でコピーした情報を入力</li>



<li><strong>Identifier (Entity ID):</strong> <code>https://your-n8n-domain.com/rest/sso/saml/metadata</code></li>



<li><strong>Reply URL (Assertion Consumer Service URL):</strong> <code>https://your-n8n-domain.com/rest/sso/saml/acs</code></li>
</ul>
</li>



<li><strong>属性とクレーム</strong>
<ul class="wp-block-list">
<li><strong>Last Name クレームを追加</strong>
<ul class="wp-block-list">
<li>Name: lastname</li>



<li>Namespace: <a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">http://schemas.xmlsoap.org/ws/2005/05/identity/claims</a></li>



<li>Source: Attribute</li>



<li>Source attribute: user.surname</li>
</ul>
</li>



<li><strong>First Name クレームを追加</strong>
<ul class="wp-block-list">
<li>Name: firstname</li>



<li>Namespace: <a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">http://schemas.xmlsoap.org/ws/2005/05/identity/claims</a></li>



<li>Source: Attribute</li>



<li>Source attribute: user.givenname</li>
</ul>
</li>



<li><strong>UPN（User Principal Name）クレームを追加</strong>
<ul class="wp-block-list">
<li>Name: upn</li>



<li>Namespace: <a href="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">http://schemas.xmlsoap.org/ws/2005/05/identity/claims</a></li>



<li>Source: Attribute</li>



<li>Source attribute: user.userprincipalname</li>
</ul>
</li>
</ul>
</li>
</ul>



<p>ex. 設定後の値</p>
</div>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="438" src="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-3-2-1024x438.png" alt="" class="wp-image-362" srcset="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-3-2-1024x438.png 1024w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-3-2-300x128.png 300w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-3-2-768x329.png 768w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-3-2-1536x658.png 1536w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-3-2.png 1906w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary><strong>💡Tips: SAML の設定項目の意味について</strong></summary>
<ul class="wp-block-list">
<li><strong><strong>基本的な SAML 構成</strong></strong>とは？
<ul class="wp-block-list">
<li><strong>Identifier (Entity ID):</strong>
<ul class="wp-block-list">
<li>N8N を一意に識別するための ID</li>



<li>「この SAML リクエストは、どのアプリケーションから来たのか？」を Entra ID が判断するために使用</li>



<li>世界中で唯一無二の識別子である必要がある</li>
</ul>
</li>



<li><strong>Reply URL (Assertion Consumer Service URL)</strong>:
<ul class="wp-block-list">
<li>認証完了後に SAML Response（認証結果）を受け取る URL</li>



<li>Entra ID が「認証成功しました！」という情報を送る先</li>



<li>セキュリティ上、この URL だけが SAML Response を受け取れる</li>
</ul>
</li>
</ul>
</li>



<li><strong>属性とクレーム</strong> とは？
<ul class="wp-block-list">
<li>SAML では 認証先のアプリ と EntraID 間でログインに必要な情報を受け渡すが、各アプリで何の情報が必要なのか異なる.</li>



<li>「認証のやり取りの中に何の情報をふくめるか？」を定義しているのクレーム</li>



<li>N8N ではクレーム情報をもとに：
<ul class="wp-block-list">
<li>ユーザーアカウントを作成/識別</li>



<li>ユーザー情報を表示</li>



<li>権限を割り当て（アプリによる）</li>
</ul>
</li>
</ul>
</li>
</ul>
</details>
</blockquote>



<h4 class="wp-block-heading">2-3. メタデータ情報の取得</h4>



<p><strong>SAML Certificates</strong> セクションから <strong>Metadata URL を取得：</strong></p>



<ol class="wp-block-list">
<li>シングルサインオンの設定画面から [SAML証明書] のセクションに移動</li>



<li><strong>App Federation Metadata Url</strong> をコピー
<ul class="wp-block-list">
<li>例: https://login.microsoftonline.com/[tenant-id]/federationmetadata/2007-06/federationmetadata.xml</li>
</ul>
</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary><strong>💡Tips: SAML メタデータとは</strong></summary>
<p><strong>Entra ID が N8N に渡す「私の情報と使い方」</strong>：</p>



<ul class="wp-block-list">
<li><code>メタデータに含まれる情報:</code>
<ul class="wp-block-list">
<li>誰か？（Entity ID / Issuer）</li>



<li>どこに認証リクエストを送るか？（SSO URL）</li>



<li>どこにログアウトリクエストを送るか？（Logout URL）</li>



<li>どうやって安全に通信するか？（証明書）</li>



<li>どんなフォーマットで通信するか？（設定）</li>
</ul>
</li>
</ul>



<p><strong>ex. 実際のメタデータのサンプル</strong></p>



<pre class="wp-block-code"><code>&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;EntityDescriptor 
  xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
  entityID="https://sts.windows.net/12345678-abcd-1234-abcd-123456789abc/">
  
  &lt;!-- 1️⃣ IDプロバイダー（Entra ID）の基本情報 -->
  &lt;IDPSSODescriptor>
    
    &lt;!-- 2️⃣ 証明書（公開鍵）- 重要！ -->
    &lt;KeyDescriptor use="signing">
      &lt;X509Certificate>
        MIIDPjCCAiqgAwI...
      &lt;/X509Certificate>
    &lt;/KeyDescriptor>
    
    &lt;!-- 3️⃣ ログインURL（認証リクエストの送信先） -->
    &lt;SingleSignOnService 
      Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
      Location="https://login.microsoftonline.com/12345678.../saml2"/>
    
    &lt;!-- 4️⃣ ログアウトURL -->
    &lt;SingleLogoutService 
      Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
      Location="https://login.microsoftonline.com/12345678.../saml2/logout"/>
    
  &lt;/IDPSSODescriptor>
&lt;/EntityDescriptor></code></pre>
</details>
</blockquote>



<h4 class="wp-block-heading">2-4. ユーザー・グループの割り当て</h4>



<ol class="wp-block-list">
<li>左メニューから Users and groups を選択</li>



<li>「ユーザーまたはグループの追加」をクリック</li>



<li>N8N にアクセスさせるユーザーまたはグループを選択</li>



<li>Assign をクリック</li>
</ol>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="438" src="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-4-1024x438.png" alt="" class="wp-image-365" srcset="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-4-1024x438.png 1024w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-4-300x128.png 300w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-4-768x329.png 768w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-4-1536x658.png 1536w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-4.png 1906w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">手順 3: N8N 側でメタデータを設定</h3>



<ol class="wp-block-list">
<li>N8N に戻り、<strong>Settings</strong> > <strong>SSO</strong> を開く</li>



<li><strong>Identity Provider Settings</strong> フィールドに Metadata URL を貼り付け</li>



<li>Activated トグルを押して SSO を有効化する</li>



<li><strong>Save</strong> をクリック</li>



<li>Test Settings ボタンをクリックし SSO がうまくいったかを確認
<ul class="wp-block-list">
<li>うまくいくと [SAML Connection Test was Successful] のメッセージがでます</li>
</ul>
</li>



<li>あとはログイン画面で SSO で sign-in するボタンが表示されます</li>
</ol>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="438" src="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-5-1024x438.png" alt="" class="wp-image-368" srcset="https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-5-1024x438.png 1024w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-5-300x128.png 300w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-5-768x329.png 768w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-5-1536x658.png 1536w, https://tech-itoya.com/wp-content/uploads/2025/10/tori-20251005-5.png 1906w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<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="350"
					data-ulike-nonce="b12efbf33a"
					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_350"></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/n8n%e3%81%ae%e3%82%a2%e3%82%ab%e3%82%a6%e3%83%b3%e3%83%88%e3%82%92entraid%e7%b5%8c%e7%94%b1%e3%81%a7%e7%ae%a1%e7%90%86%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<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[はじめに 先日ALBの前段にCloudFrontを導入した際に、4XX系のエラーが爆増する現象に遭遇しました 開発環境での確認段階で気づけたのでセーフでしたが、ある程度リクエスト量をかけないと再現しない現象で見落としそう [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">はじめに</h2>



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



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



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



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



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



<figure class="wp-block-image size-full"><img loading="lazy" 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">4XX 系エラーの原因</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 loading="lazy" 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 loading="lazy" 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">原因1. WAF の Ratelimit による 403 エラー</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">原因2. アプリケーション (laravel) API Throttle  による 429 エラー</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">Cloudfront で、CloudFront-Viewer-Address をヘッダーを付与</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">ApacheでCloudFront-Viewer-Addressを受け取り、RemoteIP に設定</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">まとめ</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="90736f86b3"
					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>



<h2 class="wp-block-heading">想定している構成</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">ポリシーの書き方</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">おまけ</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="6b46aab027"
					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>【小ネタ】Supervisorctl 実行時に unix:///tmp/supervisor.sock no such file がでてしまった</title>
		<link>https://tech-itoya.com/%e3%80%90%e5%b0%8f%e3%83%8d%e3%82%bf%e3%80%91supervisorctl-%e5%ae%9f%e8%a1%8c%e6%99%82%e3%81%ab-unix-tmp-supervisor-sock-no-such-file-%e3%81%8c%e3%81%a7%e3%81%a6%e3%81%97%e3%81%be%e3%81%a3/</link>
					<comments>https://tech-itoya.com/%e3%80%90%e5%b0%8f%e3%83%8d%e3%82%bf%e3%80%91supervisorctl-%e5%ae%9f%e8%a1%8c%e6%99%82%e3%81%ab-unix-tmp-supervisor-sock-no-such-file-%e3%81%8c%e3%81%a7%e3%81%a6%e3%81%97%e3%81%be%e3%81%a3/#respond</comments>
		
		<dc:creator><![CDATA[tori-dash]]></dc:creator>
		<pubDate>Fri, 06 Dec 2024 14:41:29 +0000</pubDate>
				<category><![CDATA[TECH記事]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://tech-itoya.com/?p=257</guid>

					<description><![CDATA[laravel のキューイング処理などで supervisor を動作させていたのですが、ある日突然 supervisorctl 実行時にエラーがでるようになってしまいました socket ファイルがなくて怒られているよ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>laravel のキューイング処理などで supervisor を動作させていたのですが、ある日突然 supervisorctl 実行時にエラーがでるようになってしまいました</p>



<pre class="wp-block-code"><code># XXX@XXX:~/app$ /usr/bin/supervisorctl status
unix:///tmp/supervisor.sock no such file</code></pre>



<p>socket ファイルがなくて怒られているようですが、今まで動いていたのに何で？と思い調べた時のメモになります。</p>



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



<p>お察しの方も多いかもしれませんが、「<strong>supervisor のソケットパスを <code>/tmp</code> 以外に指定</strong>」すれば解決しました。</p>



<p><strong><code>[unix_http_server]</code> と <code>[supervisorctl]</code></strong> に設定が必要で、具体的には以下のようになります。</p>



<pre class="wp-block-code"><code>&#91;unix_http_server]
;file=/tmp/supervisor.sock   ; デフォルトだと /tmp に作られる。
file=/var/run/supervisor.sock 
...(中略)

&#91;supervisorctl]
serverurl=unix:///var/run/supervisor.sock
...(中略)</code></pre>



<p>多くのディストリビューションで /tmp 配下のファイルは定期的にお掃除されるようになっています。</p>



<p>supervisor のデフォルト設定では /tmp 以下にソケットを作ろうとするので、折角作られたソケットファイルが掃除されてしまっていたことが原因でした。</p>



<p>今回の自分の実行環境は amazon linux 2023 で、10 日ごとに tmp ディレクトリ配下をお掃除する設定が入っていました。確認時のコマンドは以下です。</p>



<pre class="wp-block-code"><code># XXX@XXX:~/app$ /usr/lib/tmpfiles.d/tmp.conf
q /tmp 1777 root root 10d # /tmp 以下は 10 days で消える
q /var/tmp 1777 root root 30d</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="257"
					data-ulike-nonce="3ec0ac20bb"
					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_257"></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/%e3%80%90%e5%b0%8f%e3%83%8d%e3%82%bf%e3%80%91supervisorctl-%e5%ae%9f%e8%a1%8c%e6%99%82%e3%81%ab-unix-tmp-supervisor-sock-no-such-file-%e3%81%8c%e3%81%a7%e3%81%a6%e3%81%97%e3%81%be%e3%81%a3/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>



<h2 class="wp-block-heading">構成図</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">Lambda の実装</h2>



<h3 class="wp-block-heading">Lambda コード</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">その他リスナールールやS3の作成</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">おまけ: メンテナンス画面の表示方法いろいろ</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="32f81faa61"
					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>



<h2 class="wp-block-heading">結論</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">実装</h2>



<h3 class="wp-block-heading">全体のディレクトリ構成</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">tfcmt.yml の設定</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">tfwrapper.sh の設定</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">Github Actions のワークフロー</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">改善ポイント</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="2da2f930eb"
					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>
	</channel>
</rss>
