<?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>むらおか | きいちログ</title>
	<atom:link href="https://wptech.kiichiro.work/author/kiichiro-admin/feed/" rel="self" type="application/rss+xml" />
	<link>https://wptech.kiichiro.work</link>
	<description>WordPressとかAWSとかPHPとか</description>
	<lastBuildDate>Sat, 06 Dec 2025 22:56:08 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>CloudShell VPC environment で RDS に dump を流し込む</title>
		<link>https://wptech.kiichiro.work/29x6dunked/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Sat, 06 Dec 2025 22:56:08 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[AWS]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=4039</guid>

					<description><![CDATA[はじめに 結構前の話ですが VPC 内に CloudShell 環境を作ることが出来るようになってました。 Amazon VPC AWS CloudShell &#12391;&#12398; &#12398;&#203 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">はじめに</h2>



<p>結構前の話ですが VPC 内に CloudShell 環境を作ることが出来るようになってました。</p>




<a rel="noopener" href="https://docs.aws.amazon.com/ja_jp/cloudshell/latest/userguide/using-cshell-in-vpc.html" title="Amazon VPC AWS CloudShell &#12391;&#12398; &#12398;&#20351;&#29992; - AWS CloudShell" 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 decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fdocs.aws.amazon.com%2Fja_jp%2Fcloudshell%2Flatest%2Fuserguide%2Fusing-cshell-in-vpc.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">Amazon VPC AWS CloudShell &#12391;&#12398; &#12398;&#20351;&#29992; - AWS CloudShell</div><div class="blogcard-snippet external-blogcard-snippet">VPC での CloudShell の使用に関するガイダンスを提供します。これには、運用上の制約、CloudShell VPC 環境を作成する手順、作成と使用に必要な IAM アクセス許可が含まれます。</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 decoding="async" src="https://www.google.com/s2/favicons?domain=https://docs.aws.amazon.com/ja_jp/cloudshell/latest/userguide/using-cshell-in-vpc.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>従来はプライベートサブネット内の RDS へのアクセスは、Bastion host を ECS で構築しておいて ECS Exec する方法が主流だったと思いますが、CloudShell VPC environment であればコンテナを用意する必要が無いということになります。更に嬉しいことに mysql や psql コマンドが標準でインストールされています。</p>



<p>利用時の注意点として、ファイルのアップロード/ダウンロードが出来ないというのがあります。よって、RDS に dump データを流し込む際は S3 を経由するなどをする必要があります。</p>



<p>実際にやってみます。</p>



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



<p>同じ VPC 内に RDS と CloudShell を用意することになります。</p>



<p>今回は dump ファイルを持っていきたいので S3 に sql ファイルをアップロードしておきます。プライベートサブネット内から S3 へのアクセスはゲートウェイエンドポイントを介して行います。</p>



<figure class="wp-block-image aligncenter size-full"><img fetchpriority="high" decoding="async" width="841" height="461" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/cloudshell-rds-access-overview.drawio.png" alt="AWS Cloud の中の VPC にPrivate subnet がある。その中に RDS と CloudShell が入っており、相互に矢印で結線している。S3 は VPC の外に配置され、dump.sql が重なっている。CloudShell と S3 は Gateway を介して結線している。" class="wp-image-4057" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/cloudshell-rds-access-overview.drawio.png 841w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/cloudshell-rds-access-overview.drawio-300x164.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/cloudshell-rds-access-overview.drawio-768x421.png 768w" sizes="(max-width: 841px) 100vw, 841px" /></figure>



<h2 class="wp-block-heading">事前準備</h2>



<p>必要なリソースをマネジメントコンソールから作成します。</p>



<h3 class="wp-block-heading">VPC ネットワークの作成</h3>



<p>VPC とその関連リソースを作成します。作成するリソースを「VPCなど」とすることで周辺のリソースも併せて作成出来ます。</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 aligncenter size-large"><img loading="lazy" decoding="async" width="1022" height="342" data-id="4064" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/6dfb821fb3725240e12edace8c677165.png" alt="VPCの設定で作成するリソースをVPCなどにチェックを入れている" class="wp-image-4064" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/6dfb821fb3725240e12edace8c677165.png 1022w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/6dfb821fb3725240e12edace8c677165-300x100.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/6dfb821fb3725240e12edace8c677165-768x257.png 768w" sizes="(max-width: 1022px) 100vw, 1022px" /></figure>
</figure>



<p>プライベートサブネットがあれば良いのでいくつか作成しておきます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="908" height="204" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/fa3f66a41470995edb8814bd345ae1c7.png" alt="プライベートサブネットの数の選択肢の2を選択している" class="wp-image-4065" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/fa3f66a41470995edb8814bd345ae1c7.png 908w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/fa3f66a41470995edb8814bd345ae1c7-300x67.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/fa3f66a41470995edb8814bd345ae1c7-768x173.png 768w" sizes="(max-width: 908px) 100vw, 908px" /></figure>



<p>VPC エンドポイントは S3 ゲートウェイエンドポイントが必要なのでここで一緒に作成するか、別で作成する必要があります。別で作成する場合はサブネットのルートテーブルに忘れずにアタッチしておきましょう。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="940" height="286" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/b1fd6d569d1afcae6d5164957ba221a1.png" alt="VPCエンドポイントの選択肢のなしを選択している" class="wp-image-4063" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/b1fd6d569d1afcae6d5164957ba221a1.png 940w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/b1fd6d569d1afcae6d5164957ba221a1-300x91.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/b1fd6d569d1afcae6d5164957ba221a1-768x234.png 768w" sizes="(max-width: 940px) 100vw, 940px" /></figure>



<h3 class="wp-block-heading">RDS の作成</h3>



<p>今回は MySQL を使います。先程作成した VPC リソースに合わせて VPC とサブネットを選択します。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="423" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/1c7f94dbfc08d0d363cbfd07bb4e3c50-1024x423.png" alt="RDSのネットワーク設定画面" class="wp-image-4088" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/1c7f94dbfc08d0d363cbfd07bb4e3c50-1024x423.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/1c7f94dbfc08d0d363cbfd07bb4e3c50-300x124.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/1c7f94dbfc08d0d363cbfd07bb4e3c50-768x317.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/1c7f94dbfc08d0d363cbfd07bb4e3c50-1536x634.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/1c7f94dbfc08d0d363cbfd07bb4e3c50-2048x846.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>セキュリティグループを新規作成する場合は、CloudShell のセキュリティグループからのインバウンドアクセスを許可するようにする必要があります。</p>



<h3 class="wp-block-heading">S3 バケットの作成と dump ファイルのアップロード</h3>



<p>S3 バケットを用意します。パブリックアクセスは off のままで OK です。</p>



<p>dump ファイルは今回は以下のものを利用します。</p>




<a rel="noopener" href="https://dev.mysql.com/doc/index-other.html" title="https://dev.mysql.com/doc/index-other.html" 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%2Fdev.mysql.com%2Fdoc%2Findex-other.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">https://dev.mysql.com/doc/index-other.html</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.mysql.com/doc/index-other.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">dev.mysql.com</div></div></div></div></a>



<h2 class="wp-block-heading">実行</h2>



<h3 class="wp-block-heading">CloudShell の起動</h3>



<p>マネジメントコンソールにログインし、CloudShell を起動します。</p>



<p>画面上部のアイコンをクリックすると、画面下段に CloudShell が起動します。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="794" height="102" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/5d81015708b5d32611509f055cb39014.png" alt="CloudShell のアイコンが赤枠で示されている" class="wp-image-4098" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/5d81015708b5d32611509f055cb39014.png 794w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/5d81015708b5d32611509f055cb39014-300x39.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/5d81015708b5d32611509f055cb39014-768x99.png 768w" sizes="(max-width: 794px) 100vw, 794px" /></figure>



<p>アクションボタンをクリックして「Create VPC environment」を選択します。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="762" height="776" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/e8bc3a4fe2023a4fb77ba783140918b1.png" alt="Create VPC environment が赤枠で示されている" class="wp-image-4106" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/e8bc3a4fe2023a4fb77ba783140918b1.png 762w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/e8bc3a4fe2023a4fb77ba783140918b1-295x300.png 295w" sizes="(max-width: 762px) 100vw, 762px" /></figure>



<p>VPC, Subnet, Security group は事前に作成したものを選択します。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="1024" height="1010" src="https://wptech.kiichiro.work/wp-content/uploads/2025/12/ce7d1ce86ae4ff8121c58300ed140112.png" alt="Create a VPC environment の設定画面" class="wp-image-4109" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/12/ce7d1ce86ae4ff8121c58300ed140112.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/ce7d1ce86ae4ff8121c58300ed140112-300x296.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/12/ce7d1ce86ae4ff8121c58300ed140112-768x758.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">dump ファイルのダウンロード</h3>



<p>S3 にアップロードした dump ファイルをダウンロードしてきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="CloudShell" data-lang="Bash"><code>~ $ aws --version aws-cli/2.32.3 Python/3.13.9 Linux/6.1.156-177.286.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2023

# S3 への疎通確認
~ $ aws s3 ls

# dump ファイルのダウンロード 
~ $ aws s3 cp s3://{bucket-name}/world.sql ./world.sql
download: s3://{bucket-name}/world.sql to ./world.sql
~ $ ls world.sql
world.sql</code></pre></div>



<p>このとき、S3 への疎通が取れない場合は S3 ゲートウェイエンドポイントの作成に失敗しているかルートテーブルにアタッチしていないことが考えられます。</p>



<h3 class="wp-block-heading">dump データのインポート</h3>



<p>念の為、インポートの前に mysql コマンドで RDS と疎通を取ると良いでしょう。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="CloudShell" data-lang="Bash"><code>~ $ mysql -h {database-endpoint} -u {user} -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.43 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type &#39;help;&#39; or &#39;\h&#39; for help. Type &#39;\c&#39; to clear the current input statement.

MySQL [(none)]&gt;</code></pre></div>



<p>うまくログイン出来ない場合は RDS が停止しているか、RDS のセキュリティグループのインバウンドルールが漏れている可能性があります。</p>



<p>mysql コマンドを使って dump データを RDS に流し込みます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="CloudShell" data-lang="Bash"><code>~ $ mysql -h {database-endpoint} -u {user} -p &lt; ./world.sql
Enter password: </code></pre></div>



<p>インポートしたデータを確認します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="CloudShell" data-lang="Bash"><code>~ $ mysql -h {database-endpoint} -u {user} -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.0.43 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type &#39;help;&#39; or &#39;\h&#39; for help. Type &#39;\c&#39; to clear the current input statement.

MySQL [(none)]&gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| world              |
+--------------------+
5 rows in set (0.005 sec)

MySQL [(none)]&gt; use world
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [world]&gt; show tables;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+
3 rows in set (0.001 sec)

MySQL [world]&gt; select * from city limit 10;
+----+----------------+-------------+---------------+------------+
| ID | Name           | CountryCode | District      | Population |
+----+----------------+-------------+---------------+------------+
|  1 | Kabul          | AFG         | Kabol         |    1780000 |
|  2 | Qandahar       | AFG         | Qandahar      |     237500 |
|  3 | Herat          | AFG         | Herat         |     186800 |
|  4 | Mazar-e-Sharif | AFG         | Balkh         |     127800 |
|  5 | Amsterdam      | NLD         | Noord-Holland |     731200 |
|  6 | Rotterdam      | NLD         | Zuid-Holland  |     593321 |
|  7 | Haag           | NLD         | Zuid-Holland  |     440900 |
|  8 | Utrecht        | NLD         | Utrecht       |     234323 |
|  9 | Eindhoven      | NLD         | Noord-Brabant |     201843 |
| 10 | Tilburg        | NLD         | Noord-Brabant |     193238 |
+----+----------------+-------------+---------------+------------+
10 rows in set (0.001 sec)

MySQL [world]&gt; </code></pre></div>



<p>問題なくインポートされていることが確認出来ました。</p>



<h2 class="wp-block-heading">おわりに</h2>



<p>CloudShell であればすぐに起動することが出来ますし、Bastion のように使用していない時間帯に停止する必要も無いので、今回のような用途であれば手軽に始められそうです。欠点としては、操作ログが標準では残らないので、ログを取得する仕組みを自前で用意する必要があることぐらいでしょうか。</p>



<p>最近は簡単な作業であれば CLI で、インフラの構築は CDK を利用することが多く、マネジメントコンソールを開いて CloudShell を使う機会がほとんどありませんでしたが、今後は使う頻度が増えそうです。</p>



<p>ちなみにスマホからも使えれば良いと思ったのですが、現在はモバイルアプリでは使えないようでした。</p>




<a rel="noopener" href="https://docs.aws.amazon.com/ja_jp/cloudshell/latest/userguide/working-with-aws-cloudshell.html#working-with-cloudshell-in-console-mobile-application" title="AWS CloudShell &#27010;&#24565; - AWS CloudShell" 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%2Fcloudshell%2Flatest%2Fuserguide%2Fworking-with-aws-cloudshell.html%23working-with-cloudshell-in-console-mobile-application?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">AWS CloudShell &#27010;&#24565; - AWS CloudShell</div><div class="blogcard-snippet external-blogcard-snippet">AWS CloudShell インターフェイスのナビゲーションを示します。</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/cloudshell/latest/userguide/working-with-aws-cloudshell.html#working-with-cloudshell-in-console-mobile-application" 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>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Laravel Starter Kits × WorkOSでEntra IDとSSOしたときの記録</title>
		<link>https://wptech.kiichiro.work/21jheu6bf8/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Tue, 15 Jul 2025 09:36:32 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Entra ID]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3849</guid>

					<description><![CDATA[はじめに 先日、Laravel 12 から Breeze と Jetstream の代わりに Starter Kits がサポートされたということを知りました。Starter Kits では WorkOS が公式にサポー [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">はじめに</h2>



<p>先日、Laravel 12 から Breeze と Jetstream の代わりに Starter Kits がサポートされたということを知りました。Starter Kits では WorkOS が公式にサポートされ、WorkOS 経由の SSO が比較的簡単に導入できるようになっていました。</p>




<a rel="noopener" href="https://laravel.com/starter-kits" title="Starter Kits - Laravel - The PHP Framework For Web Artisans" 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://laravel.com/images/og/laravel-starter-kits.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">Starter Kits - Laravel - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</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://laravel.com/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>




<a rel="noopener" href="https://laravel.com/docs/12.x/starter-kits" title="Starter Kits - Laravel 12.x - The PHP Framework For Web Artisans" 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://laravel.com/images/og/laravel-docs-12.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">Starter Kits - Laravel 12.x - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</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://laravel.com/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>



<p>せっかくなので Laravel Breeze で作ったアプリを Starter Kits を利用して作り直し、認証を WorkOS に寄せて Entra ID で SSO ログインをしてみました。実際にやってみると、WorkOS 側で拒否されるドメインがあったり、Entra ID 側の設定でログインに失敗するといったことがあり、結構なハマりポイントが多かったです。</p>



<p>今回は、導入までにハマったポイントを中心に記録を残していきます。</p>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-check block-box">
<p>WorkOS は、Google や Microsoft などのサービスを使ったソーシャルログインや SAML 認証で SSO を実現できる認証プロバイダーです。WorkOS を利用するには WorkOS アカウントが必要です。</p>




<a rel="noopener" href="https://workos.com" title="WorkOS — Your app, Enterprise Ready." 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://cdn.prod.website-files.com/621f54116cab10f6e9215d8b/627321b887917b110d342e2b_homepage.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">WorkOS — Your app, Enterprise Ready.</div><div class="blogcard-snippet external-blogcard-snippet">Developer APIs/SDKs for Enterprise Ready features like Single Sign-On, Directory Sync, Audit Logging, and more. Get star...</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://workos.com" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">workos.com</div></div></div></div></a>
</div>



<h2 class="wp-block-heading">準備</h2>



<h3 class="wp-block-heading">Laravelアプリケーション</h3>



<p><code>laravel</code> コマンドをあらかじめインストールしておきます。<code>laravel new</code> で新しい Laravel プロジェクトを対話形式で作成できます。このとき、認証プロバイダーを選択できるので WorkOS を選んでおきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code> ┌ Which authentication provider do you prefer? ────────────────┐
 │   ○ Laravel&#39;s built-in authentication                        │
 │ › ● WorkOS (Requires WorkOS account)                         │
 └──────────────────────────────────────────────────────────────┘</code></pre></div>



<p>WorkOS の API を利用するために <code>.env</code> も編集しておきます。各値は WorkOS のダッシュボードから確認できます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>WORKOS_CLIENT_ID=xxxx
WORKOS_API_KEY=xxxx
WORKOS_REDIRECT_URL=&quot;http://localhost:8000/authenticate&quot;</code></pre></div>



<h3 class="wp-block-heading">Entra ID側の準備とSAML接続</h3>



<p>Entra ID 側で WorkOS と SAML 接続できるように設定をしておきます。手順はドキュメントの通りで大丈夫です。</p>




<a rel="noopener" href="https://workos.com/docs/integrations/entra-id-saml" title="Entra ID SAML (formerly Azure AD) – Integrations – WorkOS Docs" 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://og-images.workos.com/api/docs/?t=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXRlZ29yeSI6IkludGVncmF0aW9ucyIsInRpdGxlIjoiRW50cmEgSUQgU0FNTCAoZm9ybWVybHkgQXp1cmUgQUQpIiwiZGVzY3JpcHRpb24iOiJMZWFybiBob3cgdG8gY29uZmlndXJlIGEgY29ubmVjdGlvbiBFbnRyYSBJRCB2aWEgU0FNTCIsImlhdCI6MTc1MjA3ODY0OH0.GBl9X3yNSCpCh78HRqAaMD3klfQb9VZJDgRfNaKrDvA" 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">Entra ID SAML (formerly Azure AD) – Integrations – WorkOS Docs</div><div class="blogcard-snippet external-blogcard-snippet">Learn how to configure a connection Entra ID via SAML.</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://workos.com/docs/integrations/entra-id-saml" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">workos.com</div></div></div></div></a>



<p>ACS URL と SP Entity の場所が若干わかりづらいですが、Organizations から対応するものを選択して View connections ボタンを押下した先にあります (執筆当時)。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="166" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/3cdf05bb6012e22c0373e0b2b343b056-1024x166.png" alt="Azure SSO の organization の設定画面。Authentication のエリアに Single Sign-On ブロックがあり、View connection ボタンがある。" class="wp-image-3894" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/3cdf05bb6012e22c0373e0b2b343b056-1024x166.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/3cdf05bb6012e22c0373e0b2b343b056-300x49.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/3cdf05bb6012e22c0373e0b2b343b056-768x125.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/3cdf05bb6012e22c0373e0b2b343b056-1536x249.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/3cdf05bb6012e22c0373e0b2b343b056-2048x332.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="200" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/a0a9f54e9a52c6a8f8247663ed908919-1024x200.png" alt="Service Provider Details のブロックに SP Entity ID, ACS URL, SP Metadata が表示されている。" class="wp-image-3897" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/a0a9f54e9a52c6a8f8247663ed908919-1024x200.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/a0a9f54e9a52c6a8f8247663ed908919-300x59.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/a0a9f54e9a52c6a8f8247663ed908919-768x150.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/a0a9f54e9a52c6a8f8247663ed908919-1536x301.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/a0a9f54e9a52c6a8f8247663ed908919-2048x401.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>UI は度々変更になるようですので、見当たらなければ根気よく探す必要があります。</p>



<h2 class="wp-block-heading">ハマったポイント</h2>



<h3 class="wp-block-heading">アプリケーションへのアクセス権がなくて拒否される (AADSTS50105)</h3>



<p>アプリのログイン画面 (または AuthKit) でメールアドレスを入力して送信すると、以下のようなメッセージが表示されます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>AADSTS50105: Your administrator has configured the application HOGE SAML App (&#39;xxxxx&#39;) to block users unless they are specifically granted (&#39;assigned&#39;) access to the application. The signed in user &#39;foo@bar.example.com&#39; is blocked because they are not a direct member of a group with access, nor had access directly assigned by an administrator. Please contact your administrator to assign access to this application.</code></pre></div>



<p>これは、ログインしようとしたユーザーに当該アプリケーションへのアクセスが許可されていないことに起因します。</p>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/troubleshoot/entra/entra-id/app-integration/error-code-aadsts50105-user-not-assigned-role" title="エラー AADSTS50105 - サインインしているユーザーがアプリケーションのロールに割り当てされていません。" 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://learn.microsoft.com/en-us/media/open-graph-image.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">エラー AADSTS50105 - サインインしているユーザーがアプリケーションのロールに割り当てされていません。</div><div class="blogcard-snippet external-blogcard-snippet">Microsoft Entra SSO を使用して SAML ベースの構成済みアプリにサインインするときにAADSTS50105 エラーが発生する問題について説明します。</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://learn.microsoft.com/ja-jp/troubleshoot/entra/entra-id/app-integration/error-code-aadsts50105-user-not-assigned-role" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>Entra 管理センターまたは Azure ポータルからユーザーを追加することで解消ができるはずです。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="437" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/93666232d5217bfed963101834ddff81-1024x437.png" alt="" class="wp-image-3910" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/93666232d5217bfed963101834ddff81-1024x437.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/93666232d5217bfed963101834ddff81-300x128.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/93666232d5217bfed963101834ddff81-768x328.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/93666232d5217bfed963101834ddff81-1536x656.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/93666232d5217bfed963101834ddff81-2048x875.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">ドメインの設定漏れでSign in画面がループする</h3>



<p>WorkOS で SSO を利用するには、事前にドメインの検証と設定が必要です。この設定が漏れていると Sign in 画面から先に進めずループしてしまう挙動を示すことがあります。</p>



<p>ドメインの検証方法には、次の2つがあります。</p>



<ul class="wp-block-list">
<li>Self-serve Domain Verification</li>



<li>Manual Domain Verification</li>
</ul>



<p>詳しくは公式ドキュメントをご覧ください。</p>




<a rel="noopener" href="https://workos.com/docs/user-management/domain-verification/manual-domain-verification" title="Domain Verification – AuthKit – WorkOS Docs" 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://og-images.workos.com/api/docs/?t=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXRlZ29yeSI6IkF1dGhLaXQiLCJ0aXRsZSI6IkRvbWFpbiBWZXJpZmljYXRpb24iLCJkZXNjcmlwdGlvbiI6IlZlcmlmeSBvcmdhbml6YXRpb24gZG9tYWlucyBmb3Igc2VjdXJlIGF1dGhlbnRpY2F0aW9uIGFuZCBwcm92aXNpb25pbmciLCJpYXQiOjE3NTI0NDI3NTl9.ALGVuXT5rGp7Z0W_mHsesVdWSkJNxC3e16J8oDYUpOA" 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">Domain Verification – AuthKit – WorkOS Docs</div><div class="blogcard-snippet external-blogcard-snippet">Verify organization domains for secure authentication and provisioning.</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://workos.com/docs/user-management/domain-verification/manual-domain-verification" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">workos.com</div></div></div></div></a>



<p>Manual Domain Verification を利用する場合、既に Entra ID などで所有が確認されたカスタムドメインを、WorkOS の設定に手動で追加します。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="314" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/b3684cfc898ab16bc696b6244a8d36aa-1024x314.png" alt="" class="wp-image-3923" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/b3684cfc898ab16bc696b6244a8d36aa-1024x314.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/b3684cfc898ab16bc696b6244a8d36aa-300x92.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/b3684cfc898ab16bc696b6244a8d36aa-768x236.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/b3684cfc898ab16bc696b6244a8d36aa-1536x471.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/b3684cfc898ab16bc696b6244a8d36aa-2048x628.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>ここでは所有が確認できるドメインを入力するので、<code>gmail.com</code> や <code>outlook.com</code> などの <code>public consumer domain</code> を入力しようとすると拒否されます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="492" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/e12c23da18e887cb64deb7cbe61829e5-1024x492.png" alt="" class="wp-image-3932" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/e12c23da18e887cb64deb7cbe61829e5-1024x492.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/e12c23da18e887cb64deb7cbe61829e5-300x144.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/e12c23da18e887cb64deb7cbe61829e5-768x369.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/e12c23da18e887cb64deb7cbe61829e5.png 1224w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading">属性のマッピング不正でログインに失敗する</h3>



<p>ログインに失敗すると、Notifications に以下のログが残る場合があります。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="398" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/06f1c8289ab1f8b22d7cf6aebeaf5eda-1024x398.png" alt="" class="wp-image-3940" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/06f1c8289ab1f8b22d7cf6aebeaf5eda-1024x398.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/06f1c8289ab1f8b22d7cf6aebeaf5eda-300x117.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/06f1c8289ab1f8b22d7cf6aebeaf5eda-768x299.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/06f1c8289ab1f8b22d7cf6aebeaf5eda-1536x597.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/06f1c8289ab1f8b22d7cf6aebeaf5eda.png 1630w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>同様に Admin Email 宛に以下のようなメッセージも届いているかと思います。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>Single Sign-On failed for XXXXXX

Your foo.example.com connection received invalid user attributes and failed to authenticate a user.
 
Why this might happen
 
The user attributes are misconfigured in Entra ID (Azure AD).
A single user is misconfigured in Entra ID (Azure AD). (Check the received attributes below).
 
How to fix this
 
Verify that attribute mapping is correct in the Entra ID (Azure AD) dashboard.</code></pre></div>



<p>このエラーは、idP (今回の場合 Entra ID) から返却される attribute が WorkOS 側で期待しているものと異なっていることを示しています。</p>




<a rel="noopener" href="https://workos.com/docs/reference/sso/profile" title="API Reference – WorkOS Docs" 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://og-images.workos.com/api/docs/?t=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjYXRlZ29yeSI6IkRvY3MiLCJ0aXRsZSI6IkFQSSBSZWZlcmVuY2UiLCJkZXNjcmlwdGlvbiI6IkNvZGUgc25pcHBldHMgYW5kIHR5cGUgZGVmaW5pdGlvbnMgZm9yIHRoZSBXb3JrT1MgY2xpZW50IGxpYnJhcmllcyIsImlhdCI6MTc1MjUzNTk5OX0.BQz8GOrNy5GXy9HrECck8Z-Jcu-BLJz7fLWSr2VZ5cY" 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">API Reference – WorkOS Docs</div><div class="blogcard-snippet external-blogcard-snippet">Code snippets and type definitions for the WorkOS client libraries.</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://workos.com/docs/reference/sso/profile" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">workos.com</div></div></div></div></a>



<p>WorkOS 側で期待している属性値と Entra ID 側から送っている属性がマッチしていることを確認します。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="409" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/d2a46a061d48ce1ccc2a38c2b6de1a83-1024x409.png" alt="" class="wp-image-3949" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/d2a46a061d48ce1ccc2a38c2b6de1a83-1024x409.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/d2a46a061d48ce1ccc2a38c2b6de1a83-300x120.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/d2a46a061d48ce1ccc2a38c2b6de1a83-768x307.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/d2a46a061d48ce1ccc2a38c2b6de1a83-1536x614.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/d2a46a061d48ce1ccc2a38c2b6de1a83-2048x819.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">WorkOS Attribute mapping</figcaption></figure>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="663" src="https://wptech.kiichiro.work/wp-content/uploads/2025/07/9e957d994777d28c8e8bf4c8ce9665a3-1024x663.png" alt="" class="wp-image-3952" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/07/9e957d994777d28c8e8bf4c8ce9665a3-1024x663.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/9e957d994777d28c8e8bf4c8ce9665a3-300x194.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/9e957d994777d28c8e8bf4c8ce9665a3-768x497.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/9e957d994777d28c8e8bf4c8ce9665a3-1536x994.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/07/9e957d994777d28c8e8bf4c8ce9665a3.png 1598w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Entra 管理センター 属性とクレーム</figcaption></figure>



<p>WorkOS 側で required となっている項目については、Entra ID 側で設定されていないとキー毎何も返さなくなるので、設定しておく必要があります。特に Starter Kits を利用する場合は姓名が必須になっているので注意が必要です。</p>



<p>また、姓名を漢字などのマルチバイト文字で登録すると何故か返って来ないという現象がありました (これについては未解決です)。</p>



<h2 class="wp-block-heading">さいごに</h2>



<p>本記事では、Laravel Starter Kit で WorkOS を利用し、Microsoft Entra ID と SAML SSO を組み合わせた際に直面した問題とその対処法をまとめました。これが実際のプロジェクトで導入しようとしている方々の助けになれば幸いです。</p>



<p>今回紹介したものは、WorkOS 特有のハマりポイントもありましたが、Entra ID や SAML の仕様で躓いた部分もありました。私自身が SAML への理解が浅いということが露呈した結果ではあります。精進します。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>AWS CDK と Bref を用いた Laravel サーバーレス構成 ― Lambda + EFS + SQLite</title>
		<link>https://wptech.kiichiro.work/94slab74qm/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 30 Apr 2025 06:26:38 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Lambda]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3700</guid>

					<description><![CDATA[はじめに PHPでAWS Lambdaを利用したアプリケーションを構築するには、カスタムランタイムが必要になります。BrefはLambdaでPHPを実行するためのランタイムとLaravel、SymfonyなどのPHPフレ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">はじめに</h2>



<p>PHPでAWS Lambdaを利用したアプリケーションを構築するには、カスタムランタイムが必要になります。BrefはLambdaでPHPを実行するためのランタイムとLaravel、SymfonyなどのPHPフレームワークとの統合を提供しています。</p>




<a rel="noopener" href="https://bref.sh" title="Bref – Simple and scalable PHP with serverless" 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://bref.sh/social-card.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">Bref – Simple and scalable PHP with serverless</div><div class="blogcard-snippet external-blogcard-snippet">Bref is a framework to write and deploy serverless PHP applications on AWS Lambda.</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://bref.sh" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">bref.sh</div></div></div></div></a>



<p>本記事では、このBrefを使ったLaravelアプリケーションの環境構築方法を解説していきます。</p>



<h2 class="wp-block-heading">プロジェクトの概要</h2>



<h3 class="wp-block-heading">コンセプト</h3>



<p>基本的にBrefはServerless Frameworkを介して利用しますが、今回はAWS CDKにBref用の専用Constructが用意されているので、CDKを利用することにしました。コードベースでのインフラ管理や拡張の容易性もCDKを選定した理由になります。</p>



<p>また、個人のテスト環境として比較的低コストで構築できるように最小構成で実装していきます。</p>



<h3 class="wp-block-heading">成果物</h3>



<p>完成品は以下に配置してあります。</p>




<a rel="noopener" href="https://github.com/ShotaroMuraoka/cdk-serverless-laravel" title="GitHub - ShotaroMuraoka/cdk-serverless-laravel: Laravel アプリケーションを Bref でサーバーレス化した CDK" 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://opengraph.githubassets.com/9e5f1f036341ca90ff4e35999daa9468a3cfc0b7532da531ce456db400428a22/ShotaroMuraoka/cdk-serverless-laravel" 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">GitHub - ShotaroMuraoka/cdk-serverless-laravel: Laravel アプリケーションを Bref でサーバーレス化した CDK</div><div class="blogcard-snippet external-blogcard-snippet">Laravel アプリケーションを Bref でサーバーレス化した CDK. Contribute to ShotaroMuraoka/cdk-serverless-laravel development by creating an ac...</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://github.com/ShotaroMuraoka/cdk-serverless-laravel" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<h3 class="wp-block-heading">システム構成</h3>



<p>システム構成は以下の通りです。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="901" height="461" src="https://wptech.kiichiro.work/wp-content/uploads/2025/04/bref-cdk.png" alt="AWSの構成図。クライアントからはHTTPとaws lambda invokeの線が出ており、それぞれAPI Gateway, Lambda ConsoleFunctionと接続している。API GatewayはApiFunctionと接続している。ApiFunctionとConsoleFunctionはEFSに接続している。API GatewayはAWS Cloudの中、Lambda ApiFunctionとConsoleFunction, EFSはAWS Cloudの中のVPCの中のPrivate subnetの中に配置されている。" class="wp-image-3719" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/04/bref-cdk.png 901w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/bref-cdk-300x153.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/bref-cdk-768x393.png 768w" sizes="(max-width: 901px) 100vw, 901px" /></figure>



<p>Lambda関数が2つデプロイされているのは、API Gatewayに統合されているWebアプリケーションと <code>php artisan migrate</code> などを実行するConsoleアプリケーションを分けているためです。EFSは後述しますが、データストレージの役割を担っています。</p>



<h2 class="wp-block-heading"><strong>準備</strong></h2>



<h3 class="wp-block-heading">Laravel</h3>



<p>Laravel側の作業はBrefに必要なライブラリをインストールするだけです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>% composer require bref/bref bref/laravel-bridge --update-with-dependencies</code></pre></div>



<h3 class="wp-block-heading">CDK</h3>



<p>CDK側では、BrefのConstructパッケージをインストールします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>% npm install @bref.sh/constructs</code></pre></div>



<h2 class="wp-block-heading">CDKの実装</h2>



<p>CDK側の実装を見ていきます。</p>



<h3 class="wp-block-heading">ネットワーク</h3>



<p>EFSをマウントさせる都合上、VPC Lambdaである必要があります。コスト高になりがちなNAT Gatewayに関しては、今回はLambdaからインターネットに出ていく必要は無いので、作成をしない設定にしています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="lib/construct/networking.ts" data-lang="TypeScript"><code>const vpc = new ec2.Vpc(this, &quot;BrefVpc&quot;, {
  maxAzs: 1,
  natGateways: 0,
  restrictDefaultSecurityGroup: true,
  subnetConfiguration: [
    {
      name: &quot;BrefPrivate&quot;,
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    },
  ],
});</code></pre></div>



<h3 class="wp-block-heading">データストレージ</h3>



<p>RDBのマネージドサービスは高価ですので、Laravelの初期状態で使えるSQLiteを選定し、EFSに <code>database.sqlite</code> ファイルを配置するようにします。冗長化も不要なのでOneZoneを選択しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="lib/construct/datastore.ts" data-lang="TypeScript"><code>const fileSystem = new efs.FileSystem(this, &quot;BrefEfs&quot;, {
  vpc: vpc,
  removalPolicy: cdk.RemovalPolicy.DESTROY,
  performanceMode: efs.PerformanceMode.GENERAL_PURPOSE,
  throughputMode: efs.ThroughputMode.BURSTING,
  oneZone: true,
});

const accessPoint = this.fileSystem.addAccessPoint(&quot;BrefAccessPoint&quot;, {
  path: &quot;/bref&quot;
  createAcl: {
    ownerUid: &quot;1001&quot;,
    ownerGid: &quot;1001&quot;,
    permissions: &quot;750&quot;,
  },
  posixUser: {
    uid: &quot;1001&quot;,
    gid: &quot;1001&quot;,
  },
});</code></pre></div>



<h3 class="wp-block-heading">アプリケーション</h3>



<p>システム構成の項で紹介した通り、WebアプリケーションとConsoleアプリケーションの2つを用意しています。</p>



<h4 class="wp-block-heading">Webアプリケーション</h4>



<p>Webアプリケーションでは、Lambda関数とHTTPからのアクセス用にAPI Gatewayを統合します。Lambda関数は <code>@bref.sh/constructs</code> に <code>PhpFpmFunction</code> というBrefのレイヤーを追加したConstructがあるので、これを使用します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="lib/construct/backend.ts" data-lang="TypeScript"><code>const backendFn = new PhpFpmFunction(this, &quot;BrefApiFunction&quot;, {
  functionName: &quot;ApiFunction&quot;,
  handler: &quot;public/index.php&quot;,
  code: packagePhpCode(&quot;../laravel/&quot;, {
    exclude: [&quot;tests/**&quot;, &quot;var/**&quot;],
  }),
  timeout: cdk.Duration.seconds(28),
  memorySize: 1024,
  vpc: vpc,
  filesystem: lambda.FileSystem.fromEfsAccessPoint(
    accessPoint,
    &quot;/mnt/efs&quot;,
  ),
  environment: {
    APP_ENV: &quot;production&quot;,
    LOG_CHANNEL: &quot;stderr&quot;,
    DB_CONNECTION: &quot;sqlite&quot;,
    DB_DATABASE: &quot;/mnt/efs/database.sqlite&quot;,
  },
  phpVersion: &quot;8.4&quot;,
});

const api = new apigwv2.HttpApi(this, &quot;BrefHttpApi&quot;, {
  defaultIntegration: new integrations.HttpLambdaIntegration(
    &quot;Integration&quot;,
    backendFn,
  ),
});

new cdk.CfnOutput(this, &quot;ApiUrl&quot;, {
  value: api.url!,
});</code></pre></div>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-check block-box">
<p>URLを <code>CfnOutput</code> で出力するとデプロイ後のアクセスが楽になります。</p>
</div>



<h4 class="wp-block-heading">Consoleアプリケーション</h4>



<p>Brefには、<code>ConsoleFunction</code> というConstructがあります。これは、artisanコマンドを実行するエンドポイントになります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="lib/construct/artisan.ts" data-lang="TypeScript"><code>new ConsoleFunction(this, &quot;BrefConsoleFunction&quot;, {
  functionName: &quot;ConsoleFunction&quot;,
  handler: &quot;artisan&quot;,
  code: packagePhpCode(&quot;../laravel/&quot;, {
    exclude: [&quot;tests/**&quot;, &quot;var/**&quot;],
  }),
  timeout: cdk.Duration.seconds(28),
  memorySize: 512,
  vpc: vpc,
  filesystem: lambda.FileSystem.fromEfsAccessPoint(
    accessPoint,
    &quot;/mnt/efs&quot;,
  ),
  environment: {
    APP_ENV: &quot;production&quot;,
    LOG_CHANNEL: &quot;stderr&quot;,
    DB_CONNECTION: &quot;sqlite&quot;,
    DB_DATABASE: &quot;/mnt/efs/database.sqlite&quot;,
  },
  phpVersion: &quot;8.4&quot;,
});</code></pre></div>



<div class="wp-block-cocoon-blocks-tab-box-1 blank-box bb-tab bb-check block-box">
<p>artisanコマンドはaws cli経由で実行しますので、<code>functionName</code> を定義しておくと後々便利です。</p>
</div>



<h2 class="wp-block-heading">デプロイ・動作確認</h2>



<p>デプロイして動作確認してみます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>% cdk deploy

(略)

Outputs:
BrefStack.ApiUrl = https://example.execute-api.ap-northeast-1.amazonaws.com/
</code></pre></div>



<p>出力されたURLにアクセスしてみます。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="537" src="https://wptech.kiichiro.work/wp-content/uploads/2025/04/e6f5d404fcf83bb886fcbff5cd5339e9-1024x537.png" alt="Internal Server Error のタイトルで、Database file at path [/mnt/efs/database.sqlite] does not exist. のメッセージが出ている。" class="wp-image-3787" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/04/e6f5d404fcf83bb886fcbff5cd5339e9-1024x537.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/e6f5d404fcf83bb886fcbff5cd5339e9-300x157.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/e6f5d404fcf83bb886fcbff5cd5339e9-768x403.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/e6f5d404fcf83bb886fcbff5cd5339e9-1536x805.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/e6f5d404fcf83bb886fcbff5cd5339e9.png 1618w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>当然、migrationが終わっていないのでエラーが表示されます。ConsoleFunctionを直接実行してmigrationします。aws cliを利用して以下を実行します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>% aws lambda invoke \
     --function-name ConsoleFunction \
     --region ap-northeast-1 \
     --cli-binary-format raw-in-base64-out \
     --payload &#39;&quot;migrate --force&quot;&#39; \
     response.json</code></pre></div>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>{
    &quot;StatusCode&quot;: 200,
    &quot;ExecutedVersion&quot;: &quot;$LATEST&quot;
}
</code></pre></div>



<p>Lambdaの実行に問題がなければLaravelの画面が見られるようになるはずです。</p>



<figure class="wp-block-image aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="483" src="https://wptech.kiichiro.work/wp-content/uploads/2025/04/deca5a578e8813d0057e8e4cd7a16ea4-1024x483.png" alt="Laravel 12 の初期画面。" class="wp-image-3790" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/04/deca5a578e8813d0057e8e4cd7a16ea4-1024x483.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/deca5a578e8813d0057e8e4cd7a16ea4-300x141.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/deca5a578e8813d0057e8e4cd7a16ea4-768x362.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/deca5a578e8813d0057e8e4cd7a16ea4-1536x724.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/04/deca5a578e8813d0057e8e4cd7a16ea4.png 1934w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading"><strong>まとめと感想</strong></h2>



<p>本記事では、BrefをCDKで利用してAWS Lambda上でLaravelを動かしてみました。また、低コストでの構築を目指し、最小構成の形を取りました。</p>



<p>BrefはLaravelアプリケーション側で意識することが少ないですので、かなり手軽にできるんじゃないかと思いました。デプロイパイプラインを整えれば、本番環境にも適用できると思います。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>AWS Certified SysOps Administrator Associate合格までの勉強時間とやったことを振り返る</title>
		<link>https://wptech.kiichiro.work/92q6rj32xh/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 02 Apr 2025 05:33:17 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[AWS]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3666</guid>

					<description><![CDATA[DVA の有効期限が来年に迫っていたので、さっさと SOA を取ってしまおうと重い腰をあげてようやく受験してきました。今回も振り返りをしてみます。 どのような勉強をしたか 今回もテキストと公式資料のみを使用するという方法 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>DVA の有効期限が来年に迫っていたので、さっさと SOA を取ってしまおうと重い腰をあげてようやく受験してきました。今回も振り返りをしてみます。</p>



<h2 class="wp-block-heading">どのような勉強をしたか</h2>



<p>今回もテキストと公式資料のみを使用するという方法をとりました。</p>



<p>受験を決めてから受験当日まで日が無かったので、今までの試験と比較して勉強時間は極小になっています。</p>



<h3 class="wp-block-heading">テキスト</h3>



<p><strong>AWS認定資格試験テキスト AWS認定SysOpsアドミニストレーター &#8211; アソシエイト AWS認定資格試験テキスト</strong></p>



<figure class="wp-block-embed is-type-rich is-provider-amazon wp-block-embed-amazon"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="AWS認定資格試験テキスト AWS認定SysOpsアドミニストレーター - アソシエイト AWS認定資格試験テキスト" type="text/html" width="1320" height="550" frameborder="0" allowfullscreen style="max-width:100%" src="https://read.amazon.com.au/kp/card?preview=inline&#038;linkCode=df0&#038;ref_=k4w_oembed_ssB8YP1DkFbtOo&#038;asin=481560908X&#038;tag=jpgo-22"></iframe>
</div></figure>



<p>昨年 SAP の前に受験する前に購入していたものですが、試験内容は当時から変更が無いようなのでそのまま利用しました。このシリーズは鉄板だと思います。</p>



<p>メインで利用するというより、後述の模擬試験実施後に知識が不足している分野を読み込むという使い方をしました。</p>



<h3 class="wp-block-heading">公式</h3>



<h4 class="wp-block-heading">Skill Builder</h4>



<p>Skill Builder では、以下の2コース (無料) を実施しました。</p>



<p><strong>AWS Certified SysOps Administrator &#8211; Associate Official Practice Question Set (SOA-C02 &#8211; 日本語)</strong></p>




<a rel="noopener" href="https://explore.skillbuilder.aws/learn/courses/12555/aws-certified-sysops-administrator-associate-official-practice-question-set-soa-c02-ri-ben-yu" title="Self-paced digital training on AWS - AWS Skill Builder" 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%2Fexplore.skillbuilder.aws%2Flearn%2Fcourses%2F12555%2Faws-certified-sysops-administrator-associate-official-practice-question-set-soa-c02-ri-ben-yu?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">Self-paced digital training on AWS - AWS Skill Builder</div><div class="blogcard-snippet external-blogcard-snippet">Your learning center to build in-demand cloud skills.</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://explore.skillbuilder.aws/learn/courses/12555/aws-certified-sysops-administrator-associate-official-practice-question-set-soa-c02-ri-ben-yu" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">explore.skillbuilder.aws</div></div></div></div></a>



<p><strong>Exam Prep Standard Course: AWS Certified SysOps Administrator &#8211; Associate (SOA-C02) (日本語実写版)</strong></p>




<a rel="noopener" href="https://explore.skillbuilder.aws/learn/courses/14901/exam-prep-standard-course-aws-certified-sysops-administrator-associate-soa-c02-ri-ben-yu-shi-xie-ban" title="Self-paced digital training on AWS - AWS Skill Builder" 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%2Fexplore.skillbuilder.aws%2Flearn%2Fcourses%2F14901%2Fexam-prep-standard-course-aws-certified-sysops-administrator-associate-soa-c02-ri-ben-yu-shi-xie-ban?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">Self-paced digital training on AWS - AWS Skill Builder</div><div class="blogcard-snippet external-blogcard-snippet">Your learning center to build in-demand cloud skills.</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://explore.skillbuilder.aws/learn/courses/14901/exam-prep-standard-course-aws-certified-sysops-administrator-associate-soa-c02-ri-ben-yu-shi-xie-ban" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">explore.skillbuilder.aws</div></div></div></div></a>



<p>まずはこれらを実施し、不明瞭な分野をテキストや公式ドキュメントで補完するという方法を取りました。</p>



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



<p>なんとか合格しましたが、準備不足だったなという感がありました。特に選択肢の絞り込みでは、最後の2択を選び切れないというケースが多々あり、もう一回受けて合格できる自信はないです。</p>



<p>順序的に DOP を次に計画しているのですが、3ヶ月くらいはしっかり準備をしたいと思います。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Microsoft Certified: Azure Fundamentals (AZ-900) 合格までにやったことを振り返る</title>
		<link>https://wptech.kiichiro.work/16fmxdyyjh/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 22 Jan 2025 15:24:26 +0000</pubDate>
				<category><![CDATA[その他]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3633</guid>

					<description><![CDATA[昨年から Azure を使うようになりました。 ある分野の知識を体系的に学んで身につけるには、資格取得へ向けて勉強するのが良いというのを身を以て知っているので、とりあえず基礎的な AZ-900 を受験してきました。 Mi [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>昨年から Azure を使うようになりました。</p>



<p>ある分野の知識を体系的に学んで身につけるには、資格取得へ向けて勉強するのが良いというのを身を以て知っているので、とりあえず基礎的な AZ-900 を受験してきました。</p>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/credentials/certifications/azure-fundamentals/?practice-assessment-type=certification" title="Microsoft 認定: Azure Fundamentals - Certifications" 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%2Flearn.microsoft.com%2Fja-jp%2Fcredentials%2Fcertifications%2Fazure-fundamentals%2F%3Fpractice-assessment-type%3Dcertification?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">Microsoft 認定: Azure Fundamentals - Certifications</div><div class="blogcard-snippet external-blogcard-snippet">クラウドの概念、Azure のコア サービス、および Azure の管理とガバナンスの機能とツールに関する基本的な知識を示します。</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://learn.microsoft.com/ja-jp/credentials/certifications/azure-fundamentals/?practice-assessment-type=certification" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>難易度的には基礎レベルなので、おそらく AWS Certified Cloud Practitioner と同程度と思っています。</p>



<h2 class="wp-block-heading">どのような勉強をしたか</h2>



<p>いつものように書籍を初手で購入したのですが、Microsoft に関連するものは公式の資料やトレーニング用コンテンツが豊富なので、それだけでも良かった気がします。</p>



<h3 class="wp-block-heading">公式</h3>



<h4 class="wp-block-heading">Microsoft Learn</h4>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp" title="Microsoft Learn: キャリアの扉を開くスキルを身につける" 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%2Flearn.microsoft.com%2Fja-jp?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">Microsoft Learn: キャリアの扉を開くスキルを身につける</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://learn.microsoft.com/ja-jp" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>各認定試験ごとにコースが用意されているようで、今回は AZ-900 受験に向けたコースを受講しました。もちろん無料です。</p>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/training/courses/az-900t00" title="AZ-900T00-A コース: クラウド インフラストラクチャの概要 - Training" 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%2Flearn.microsoft.com%2Fja-jp%2Ftraining%2Fcourses%2Faz-900t00?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">AZ-900T00-A コース: クラウド インフラストラクチャの概要 - Training</div><div class="blogcard-snippet external-blogcard-snippet">AZ-900T00-A コース: クラウド インフラストラクチャの概要</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://learn.microsoft.com/ja-jp/training/courses/az-900t00" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>コースではサンドボックス内でのハンズオンが用意されています。手を動かしながら覚えられるのは地味に嬉しいですね。</p>



<h4 class="wp-block-heading">AWS プロフェッショナル向け Azure</h4>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/azure/architecture/aws-professional" title="AWS プロフェッショナルのための Azure - Azure Architecture Center" 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%2Flearn.microsoft.com%2Fja-jp%2Fazure%2Farchitecture%2Faws-professional?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">AWS プロフェッショナルのための Azure - Azure Architecture Center</div><div class="blogcard-snippet external-blogcard-snippet">Microsoft Azure プラットフォーム、アカウント、サービスの基本について説明します。 AWS プラットフォームと Azure プラットフォームの主な類似点と相違点について説明します。</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://learn.microsoft.com/ja-jp/azure/architecture/aws-professional" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>既に AWS を利用している人向けの資料です。これはかなり利用しました。よく知らない Azure のサービス名が出てきたら、まずはこのページを開くぐらいのことはやっていました。</p>



<h3 class="wp-block-heading">テキスト</h3>



<h4 class="wp-block-heading">全体像と用語がよくわかる! Microsoft Azure入門ガイド</h4>



<figure class="wp-block-embed aligncenter is-type-rich is-provider-amazon wp-block-embed-amazon"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="全体像と用語がよくわかる! Microsoft Azure入門ガイド" type="text/html" width="1320" height="550" frameborder="0" allowfullscreen style="max-width:100%" src="https://read.amazon.com.au/kp/card?preview=inline&#038;linkCode=kpd&#038;ref_=k4w_oembed_TK8nqcviMkeMiG&#038;asin=4863543689&#038;tag=kpembed-20"></iframe>
</div></figure>



<p>Azure についての知識が0からのスタートだったので、全体を薄く解説している本を用意しようと思っていました。これと「AWS プロフェッショナル向け Azure」を行き来しながら読むと良いと思います。</p>



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



<p>今まで受験した AWS 認定と比較してもかなり易しかったと思います。クラウドの基礎についての理解と Azure のメインのサービスを触ったことがあれば比較的簡単に合格できそうです。勉強時間はトータルで10時間程度だったと思います。</p>



<p>公式のコンテンツは AWS よりも日本語訳が進んでいるようですが、訳し方にムラがあるように思えます。「Azure Fundamentals」と「Azure の基礎」はどちらかに統一してもらいたかった。。</p>



<p>次も何か受けてみようと思います。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Azure】Microsoft Entra IDで認可してBlob Storageを操作してみた</title>
		<link>https://wptech.kiichiro.work/14rqfd1ec6/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 15 Jan 2025 16:39:45 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Remix]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3530</guid>

					<description><![CDATA[Azure Functions などのコンピュートリソースを用意しないで、直接 Blob Storage にファイルをアップロードする方法は無いものか、と考えていたのですが、Microsoft Entra ID で認可で [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Azure Functions などのコンピュートリソースを用意しないで、直接 Blob Storage にファイルをアップロードする方法は無いものか、と考えていたのですが、Microsoft Entra ID で認可できればいいみたいでした。</p>



<p>別解として SAS トークンを利用した認可もありますが、どうやら Entra ID が推奨みたいです。</p>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-blob-javascript-get-started?tabs=typescript%2Cazure-ad#authorize-access-and-connect-to-blob-storage" title="Azure Blob Storage と JavaScript または TypeScript の概要 - Azure Storage" 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%2Flearn.microsoft.com%2Fja-jp%2Fazure%2Fstorage%2Fblobs%2Fstorage-blob-javascript-get-started%3Ftabs%3Dtypescript%252Cazure-ad%23authorize-access-and-connect-to-blob-storage?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">Azure Blob Storage と JavaScript または TypeScript の概要 - Azure Storage</div><div class="blogcard-snippet external-blogcard-snippet">Azure Blob Storage で動作する JavaScript または TypeScript アプリケーションの開発を開始します。 この記事では、プロジェクトを設定し、Azure Blob Storage エンドポイントへのアクセス...</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://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-blob-javascript-get-started?tabs=typescript%2Cazure-ad#authorize-access-and-connect-to-blob-storage" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>というわけで、今回は Microsoft Entra ID で認可して Blob Storage にファイルをアップロードするところまでやってみます。</p>



<div class="wp-block-cocoon-blocks-icon-box common-icon-box block-box information-box">
<p>本記事紹介している方法は、Microsoft Entra ID やストレージアカウントの RBAC の設定が甘いと思われます。本番環境で利用する場合は十分なレビューが必要です。</p>
</div>



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



<p>Microsoft Entra ID での認証自体は Remix で作った Web アプリを経由させ、Blob Storage へのアップロードはブラウザから直接行うようにします。登場人物をまとめた図は以下です。</p>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="402" height="452" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/blob-upload.png" alt="ブラウザからMicrosoft Entra IDで認証してBlob Storageにファイルをアップロードするまでの構成図" class="wp-image-3540" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/blob-upload.png 402w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/blob-upload-267x300.png 267w" sizes="(max-width: 402px) 100vw, 402px" /></figure>



<h2 class="wp-block-heading">Microsoft Entra 管理センター</h2>



<p>Microsoft Entra 管理センターにて、アプリの登録を行います。アプリの登録ではアプリを一意に示す ID とアプリ自体の認証情報を作成します。</p>



<p>手順は以下を参考に。</p>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/entra/identity-platform/quickstart-web-app-nodejs-sign-in" title="クイック スタート - サンプル Web アプリでユーザーをサインインする - Microsoft identity platform" 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%2Flearn.microsoft.com%2Fja-jp%2Fentra%2Fidentity-platform%2Fquickstart-web-app-nodejs-sign-in?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">クイック スタート - サンプル Web アプリでユーザーをサインインする - Microsoft identity platform</div><div class="blogcard-snippet external-blogcard-snippet">従業員テナントの従業員または外部テナントの顧客をサインインさせるサンプル Web アプリを構成する方法を示す Web アプリのクイック スタート</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://learn.microsoft.com/ja-jp/entra/identity-platform/quickstart-web-app-nodejs-sign-in" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>アプリの登録が完了すると、概要ペインからクライアント ID とテナント ID を取得できます。</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="302" data-id="3552" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/93bccbc237574d85974089b4572968f8-1024x302.png" alt="クライアントIDとテナントID" class="wp-image-3552" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/93bccbc237574d85974089b4572968f8-1024x302.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/93bccbc237574d85974089b4572968f8-300x88.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/93bccbc237574d85974089b4572968f8-768x227.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/93bccbc237574d85974089b4572968f8-1536x453.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/93bccbc237574d85974089b4572968f8-2048x604.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p>今回は Web アプリ (機密クライアント) を選択したので、クライアントシークレットを生成します。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="209" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/d9b8ab378c3c7b947e88bb30c75b8b13-1024x209.png" alt="" class="wp-image-3557" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/d9b8ab378c3c7b947e88bb30c75b8b13-1024x209.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/d9b8ab378c3c7b947e88bb30c75b8b13-300x61.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/d9b8ab378c3c7b947e88bb30c75b8b13-768x157.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/d9b8ab378c3c7b947e88bb30c75b8b13-1536x314.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/d9b8ab378c3c7b947e88bb30c75b8b13-2048x418.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>




<a rel="noopener" href="https://learn.microsoft.com/ja-jp/entra/identity-platform/msal-client-applications" title="パブリックおよび機密のクライアント アプリ (MSAL) - Microsoft identity platform" 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%2Flearn.microsoft.com%2Fja-jp%2Fentra%2Fidentity-platform%2Fmsal-client-applications?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">パブリックおよび機密のクライアント アプリ (MSAL) - Microsoft identity platform</div><div class="blogcard-snippet external-blogcard-snippet">Microsoft Authentication Library (MSAL) でのパブリック クライアント アプリケーションと機密クライアント アプリケーションについて説明します。</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://learn.microsoft.com/ja-jp/entra/identity-platform/msal-client-applications" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">learn.microsoft.com</div></div></div></div></a>



<p>ログイン正常完了時にトークンを送る先をリダイレクト URI として追加します。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="398" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/cfdf6cc48710ac1722fe7cf50ae401fe-1024x398.png" alt="" class="wp-image-3553" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/cfdf6cc48710ac1722fe7cf50ae401fe-1024x398.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/cfdf6cc48710ac1722fe7cf50ae401fe-300x117.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/cfdf6cc48710ac1722fe7cf50ae401fe-768x299.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/cfdf6cc48710ac1722fe7cf50ae401fe-1536x598.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/cfdf6cc48710ac1722fe7cf50ae401fe-2048x797.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">ストレージアカウント</h2>



<p>Microsoft Entra ID で認証されたユーザーに Blob Storage の操作を許可します。これを実現するには、ロールベースのアクセス制御である Azure RBAC を利用します。</p>



<p>まずは Azure portal から任意のストレージアカウントとコンテナーを作成します。作成できたら、アクセス制御 (IAM) ペインから 追加タブ > ロールの割り当ての追加 を選択します。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="461" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/696c2c36a841fe44d5a74a6bdf46fff7-1024x461.png" alt="ロールの割り当ての追加" class="wp-image-3571" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/696c2c36a841fe44d5a74a6bdf46fff7-1024x461.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/696c2c36a841fe44d5a74a6bdf46fff7-300x135.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/696c2c36a841fe44d5a74a6bdf46fff7-768x346.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/696c2c36a841fe44d5a74a6bdf46fff7-1536x692.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/696c2c36a841fe44d5a74a6bdf46fff7-2048x923.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>データ共同作業者を選択して次へを選択します。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="430" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/331b7821a9217c0959e15fe949e8a734-1024x430.png" alt="" class="wp-image-3573" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/331b7821a9217c0959e15fe949e8a734-1024x430.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/331b7821a9217c0959e15fe949e8a734-300x126.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/331b7821a9217c0959e15fe949e8a734-768x323.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/331b7821a9217c0959e15fe949e8a734-1536x645.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/331b7821a9217c0959e15fe949e8a734-2048x860.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>許可するユーザーを追加してレビューと割り当てを選択します。</p>



<h3 class="wp-block-heading">Remix アプリ</h3>



<p>Remix で作ったものは GitHub にアップしてます。</p>




<a rel="noopener" href="https://github.com/ShotaroMuraoka/azure-entra-auth-blob-storage" title="GitHub - ShotaroMuraoka/azure-entra-auth-blob-storage: Azure Blob Storage へのアクセスを Microsoft Entra ID で認可するアプリ" 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%2Fgithub.com%2FShotaroMuraoka%2Fazure-entra-auth-blob-storage?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">GitHub - ShotaroMuraoka/azure-entra-auth-blob-storage: Azure Blob Storage へのアクセスを Microsoft Entra ID で認可するアプリ</div><div class="blogcard-snippet external-blogcard-snippet">Azure Blob Storage へのアクセスを Microsoft Entra ID で認可するアプリ - ShotaroMuraoka/azure-entra-auth-blob-storage</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://github.com/ShotaroMuraoka/azure-entra-auth-blob-storage" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>以降はポイントのみを説明していきます。</p>



<h4 class="wp-block-heading">ライブラリ</h4>



<p>Remix で Microsoft Entra ID を使った認証と Blob Storage の操作を行うのに以下のライブラリを利用しています。</p>



<ul class="wp-block-list">
<li>remix-auth: 3.7.0</li>



<li>remix-auth-microsoft: 2.0.1</li>



<li>remix-auth-oauth2: 1.11.0</li>



<li>@azure-storage-blob: 12.26.0</li>
</ul>



<h4 class="wp-block-heading">環境変数</h4>



<p><code>.env</code> を作成し、取得した ID とクライアントシークレット、ストレージアカウント名などを記載します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file=".env"><code>ENTRA_CLIENT_ID={クライアントID}
ENTRA_CLIENT_SECRET={クライアントシークレット}
ENTRA_REDIRECT_URI=http://localhost:5173/auth/microsoft/callback
ENTRA_TENANT_ID={テナントID}
AZURE_STORAGE_ACCCOUNT={ストレージアカウント名}
AZURE_BLOB_CONTAINER={コンテナー名}</code></pre></div>



<h4 class="wp-block-heading">認証・認可</h4>



<p><code>remix-auth-microsoft</code> では MicrosoftStrategy が用意されているので、それを利用した Authenticator を作っています。</p>



<p>ここでは、scope に Azure Storage のアクセストークンの要求を追加しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="app/services/auth.server.ts" data-lang="TypeScript" data-line="11"><code>let microsoftStrategy = new MicrosoftStrategy(
  {
    clientId: process.env.ENTRA_CLIENT_ID || &quot;&quot;,
    clientSecret: process.env.ENTRA_CLIENT_SECRET || &quot;&quot;,
    redirectUri: process.env.ENTRA_REDIRECT_URI || &quot;&quot;,
    tenantId: process.env.ENTRA_TENANT_ID || &quot;&quot;,
    scope: [
      &quot;openid&quot;,
      &quot;email&quot;,
      &quot;profile&quot;,
      &quot;https://storage.azure.com/user_impersonation&quot;,
    ], // optional
    prompt: &quot;login&quot;, // optional
  },</code></pre></div>



<p>取得したアクセストークンはセッション情報に書き出されるので、loader でフロントエンドに渡しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="app/routes/dashboard.tsx" data-lang="TypeScript" data-line="7"><code>export async function loader({ request }: LoaderFunctionArgs) {
  const user = await authenticator.isAuthenticated(request);
  if (!user) {
    throw new Response(&quot;Unauthorized&quot;, { status: 403 });
  }

  const accessToken = user.accessToken;
  const storageAccount = process.env.AZURE_STORAGE_ACCCOUNT;
  const blobContainer = process.env.AZURE_BLOB_CONTAINER
  return { accessToken, storageAccount, blobContainer };
}
</code></pre></div>



<p>アクセストークンは <code>@azure-storage-blob</code> が提供するクライアントの生成に利用しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="app/routes/dashboard.tsx" data-lang="TypeScript" data-line="2"><code>const blobServiceClient = createBlobServiceClient(
  accessToken,
  storageAccount,
);

const containerClient = blobServiceClient.getContainerClient(blobContainer);
const blockBlobClient = containerClient.getBlockBlobClient(file.name);

try {
  const response = await blockBlobClient.uploadBrowserData(file, {
    blobHTTPHeaders: { blobContentType: file.type },
  });
  console.log(&quot;upload succeeded&quot;, response.requestId);
  alert(&quot;ファイルアップロード完了&quot;);
} catch (error) {
  console.error(&quot;upload failed&quot;, error);
  alert(&quot;ファイルアップロード失敗&quot;);
}</code></pre></div>



<h2 class="wp-block-heading">動作確認</h2>



<p>アプリを起動します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code># npm run dev </code></pre></div>



<p>ブラウザで <code>http://localhost:5173/login</code> にアクセスします。</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="437" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/4ac2c4d89027686851bc700e533bd679-1024x437.png" alt="" class="wp-image-3610" style="width:499px;height:auto" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/4ac2c4d89027686851bc700e533bd679-1024x437.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/4ac2c4d89027686851bc700e533bd679-300x128.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/4ac2c4d89027686851bc700e533bd679-768x327.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/4ac2c4d89027686851bc700e533bd679.png 1032w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>ログインボタンを押下すると、Microsoft のサインイン画面にリダイレクトされます。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="976" height="742" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/a210c4a89a86a2f1249080781e3bd2c1.png" alt="" class="wp-image-3612" style="width:422px;height:auto" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/a210c4a89a86a2f1249080781e3bd2c1.png 976w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/a210c4a89a86a2f1249080781e3bd2c1-300x228.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/a210c4a89a86a2f1249080781e3bd2c1-768x584.png 768w" sizes="(max-width: 976px) 100vw, 976px" /></figure>



<p>ストレージアカウントで許可したユーザーの認証情報を入力してログインに成功すると、ダッシュボード画面にリダイレクトします。初回ログイン時にのみ、Azure Storage へのアクセス許可を聞かれます。</p>



<figure class="wp-block-image aligncenter size-large is-resized"><img loading="lazy" decoding="async" width="1024" height="641" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/7963341bc5a5350c17c2cf5915d87052-1024x641.png" alt="" class="wp-image-3614" style="width:486px;height:auto" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/7963341bc5a5350c17c2cf5915d87052-1024x641.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/7963341bc5a5350c17c2cf5915d87052-300x188.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/7963341bc5a5350c17c2cf5915d87052-768x481.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/7963341bc5a5350c17c2cf5915d87052.png 1138w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>任意のファイルを選択して Upload ボタンを押下します。ファイルのアップロードが成功するとメッセージが表示されます。</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="1012" height="510" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/c52d0320f5e473f648f6bec45c03dcd3.png" alt="" class="wp-image-3617" style="width:484px;height:auto" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/c52d0320f5e473f648f6bec45c03dcd3.png 1012w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/c52d0320f5e473f648f6bec45c03dcd3-300x151.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/c52d0320f5e473f648f6bec45c03dcd3-768x387.png 768w" sizes="(max-width: 1012px) 100vw, 1012px" /></figure>



<p>Blob コンテナーを見てみると、ファイルがアップロードされていることが確認できます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="353" src="https://wptech.kiichiro.work/wp-content/uploads/2025/01/68f3872b98c096bfebd9e36277eccf5c-1024x353.png" alt="" class="wp-image-3619" srcset="https://wptech.kiichiro.work/wp-content/uploads/2025/01/68f3872b98c096bfebd9e36277eccf5c-1024x353.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/68f3872b98c096bfebd9e36277eccf5c-300x103.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/68f3872b98c096bfebd9e36277eccf5c-768x264.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/68f3872b98c096bfebd9e36277eccf5c-1536x529.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2025/01/68f3872b98c096bfebd9e36277eccf5c.png 1766w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">おわりに</h2>



<p>普段、Azure や Entra ID を利用する機会が無いので、難易度が高かったです。次は SAS トークンを試してみようと思います。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PHP Conference Japan 2024 に LT しに行ってきました</title>
		<link>https://wptech.kiichiro.work/42h61ra7k3/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 25 Dec 2024 15:46:31 +0000</pubDate>
				<category><![CDATA[その他]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3485</guid>

					<description><![CDATA[PHP カンファレンスに参加してきました。現地参加はかなり久々でした。今回は LT に採択されたのでセッションを聞くだけではなく、登壇するという今までとは違った参加の仕方になりました。 見たセッション 当日はオープニング [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>PHP カンファレンスに参加してきました。現地参加はかなり久々でした。<br>今回は LT に採択されたのでセッションを聞くだけではなく、登壇するという今までとは違った参加の仕方になりました。</p>



<h2 class="wp-block-heading">見たセッション</h2>



<p>当日はオープニングと廣川氏のトークを配信で見ながら電車で向かいました。</p>



<p>現地に到着してからはいくつかセッションを見て回りました。特に印象に残ったものの感想を書いておきます。</p>



<h3 class="wp-block-heading">Rustで作るPHP拡張モジュール：PSR-7ライブラリ編</h3>




<a rel="noopener" href="https://fortee.jp/phpcon-2024/proposal/6fb6132e-bbff-438c-b3d0-07d903a9cf51" title="Rustで作るPHP拡張モジュール：PSR-7ライブラリ編" 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%2Ffortee.jp%2Fphpcon-2024%2Fproposal%2F6fb6132e-bbff-438c-b3d0-07d903a9cf51?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">Rustで作るPHP拡張モジュール：PSR-7ライブラリ編</div><div class="blogcard-snippet external-blogcard-snippet">Rustは高いパフォーマンスとメモリ安全性を両立したプログラミング言語で、最近ではLinuxカーネルの開発に一部取り入れられたことでも話題になるなど、人気の高い言語の一つです。そのRustで、PHPの拡張モジュールを作ることができるのをご存...</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://fortee.jp/phpcon-2024/proposal/6fb6132e-bbff-438c-b3d0-07d903a9cf51" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">fortee.jp</div></div></div></div></a>



<p>Rust には興味はあったものの、きっかけが掴めず今日までいました。PHP に関することで使えるならやってみようかなと思いました。</p>



<h3 class="wp-block-heading">見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理</h3>




<a rel="noopener" href="https://fortee.jp/phpcon-2024/proposal/6dadacb3-51b7-4a71-910a-f5f71f75e1e3" title="見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理" 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%2Ffortee.jp%2Fphpcon-2024%2Fproposal%2F6dadacb3-51b7-4a71-910a-f5f71f75e1e3?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">見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理</div><div class="blogcard-snippet external-blogcard-snippet">PHP 8.4で追加された `pg_result_memory_size()` は、SQL実行結果の中でも `memory_get_usage()` に計上されない隠れたメモリ使用量を可視化します。特に大量データ処理時のメモリ不足リスクを軽...</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://fortee.jp/phpcon-2024/proposal/6dadacb3-51b7-4a71-910a-f5f71f75e1e3" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">fortee.jp</div></div></div></div></a>



<p>普段、意識することのない PHP のメモリ管理のお話で非常に勉強になりました。php-src へのコントリビューションのヒントも得られました。</p>



<h2 class="wp-block-heading">LTで発表した話</h2>



<p>LT させてもらいました。</p>



<figure class="wp-block-embed is-type-rich is-provider-speaker-deck wp-block-embed-speaker-deck wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Opcodeを読んでいたら何故かphp-srcを読んでいた話" id="talk_frame_1299305" class="speakerdeck-iframe" src="//speakerdeck.com/player/379718951c7a4a0885023f48aaf93005" width="1320" height="742" style="aspect-ratio:1320/742; border:0; padding:0; margin:0; background:transparent;" frameborder="0" allowtransparency="true" allowfullscreen="allowfullscreen"></iframe>
</div></figure>



<p>LT に関しては、プロポーザル提出する前から果たしてこのネタは面白いのか？、技術的に間違ったことを書いて無いか？、という葛藤を100週くらいしており、応募のボタンをなかなか押せないでいました。<br>最終的には、悩むのは採択されてからにしようという考えに至り、締切ギリギリにて提出が完了しました。無事採択されてからは夜も眠れないほど悩みました。</p>



<p>ただ、そんなことは杞憂でしたね。当日の X のタイムラインでは多くの方に反応をいただけていたようです。</p>



<figure class="wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter"><div class="wp-block-embed__wrapper">
<blockquote class="twitter-tweet" data-width="550" data-dnt="true"><p lang="ja" dir="ltr">LTあんまり自信なかったけど、反応が結構あったのでやって良かったなと</p>&mdash; しおたろ (@muraSHOTARO) <a href="https://twitter.com/muraSHOTARO/status/1870791522716963322?ref_src=twsrc%5Etfw">December 22, 2024</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</div></figure>



<p>月並みな感想ですが、やってよかったです。</p>



<p>今回のネタは int の型キャストと intval だけでしたが、Opcode の最適化はそこかしこで行われているので、自分の調べた範囲のものはいずれポストしようと思います。</p>



<h2 class="wp-block-heading">感想</h2>



<p>やっぱ現地参加は良かったです。新しいつながりもできますし、あの空気感はオンラインでは味わえないです。来年も何かしら現地参加してみようと思います。</p>



<p>あと、ほぼ最初から最後までいたのですが、午前中に張り切りすぎて午後は集中力が切れてました。。体力をもっとつけようと思いました。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【Laravel】Laravel SanctumがCSRFトークンの検証で何やってるかをちゃんと理解する</title>
		<link>https://wptech.kiichiro.work/760fab7xr3/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Thu, 19 Dec 2024 14:41:21 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[コードリーディング]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3254</guid>

					<description><![CDATA[この記事は Laravel Advent Calendar 2024 19日目の投稿です。 Laravel - Qiita Advent Calendar 2024 - QiitaCalendar page for Qi [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>この記事は Laravel Advent Calendar 2024 19日目の投稿です。</p>




<a rel="noopener" href="https://qiita.com/advent-calendar/2024/laravel" title="Laravel - Qiita Advent Calendar 2024 - 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://s.wordpress.com/mshots/v1/https%3A%2F%2Fqiita.com%2Fadvent-calendar%2F2024%2Flaravel?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">Laravel - Qiita Advent Calendar 2024 - Qiita</div><div class="blogcard-snippet external-blogcard-snippet">Calendar page for Qiita Advent Calendar 2024 regarding Laravel.</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/advent-calendar/2024/laravel" 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>Laravel Sanctum が簡単な認証システムを提供してくれるのは良く知られているんですが、認証に関する面倒なことを考えずに済む程度には抽象化されてしまっていて、裏で何をやっているのかを考えたことがありませんでした。</p>



<p>今回はそんな Laravel Sanctum が CSRF トークンの検証で何をやっているのかを追っていこうと思います。</p>



<div class="wp-block-cocoon-blocks-icon-box common-icon-box block-box information-box">
<p>Laravel Sanctum のインストールについては公式ドキュメントに記載があります。詳細はそちらを御覧ください。</p>




<a rel="noopener" href="https://laravel.com/docs/11.x/sanctum#installation" title="Laravel Sanctum - Laravel 11.x - The PHP Framework For Web Artisans" 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%2Flaravel.com%2Fdocs%2F11.x%2Fsanctum%23installation?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">Laravel Sanctum - Laravel 11.x - The PHP Framework For Web Artisans</div><div class="blogcard-snippet external-blogcard-snippet">Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing ...</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://laravel.com/docs/11.x/sanctum#installation" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">laravel.com</div></div></div></div></a>
</div>



<h2 class="wp-block-heading">Middleware</h2>



<p>Sanctum インストール時に <code>bootstrap/app.php</code> に以下の記述を追加したと思います。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="bootstrap/app.php" data-lang="PHP"><code>-&gt;withMiddleware(function (Middleware $middleware) {
    $middleware-&gt;statefulApi();
})</code></pre></div>



<p>これは、特定のミドルウェアの有効化フラグを true にします。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Configuration/Middleware.php" data-lang="PHP"><code>/**
 * Indicate that Sanctum&#39;s frontend state middleware should be enabled.
 *
 * @return $this
 */
public function statefulApi()
{
    $this-&gt;statefulApi = true;

    return $this;
}</code></pre></div>



<p>ここが true になっていると、api ミドルウェアグループを利用してるエンドポイントは EnsureFrontendRequestsAreStateful というミドルウェアを通るようになります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Configuration/Middleware.php" data-lang="PHP" data-line="19"><code>/**
 * Get the middleware groups.
 *
 * @return array
 */
public function getMiddlewareGroups()
{
    $middleware = [
        &#39;web&#39; =&gt; array_values(array_filter([
            \Illuminate\Cookie\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            $this-&gt;authenticatedSessions ? &#39;auth.session&#39; : null,
        ])),
        &#39;api&#39; =&gt; array_values(array_filter([
            $this-&gt;statefulApi ? \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class : null,
            $this-&gt;apiLimiter ? &#39;throttle:&#39;.$this-&gt;apiLimiter : null,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ])),
    ];
(省略)
}</code></pre></div>



<p>EnsureFrontendRequestsAreStateful では複数のミドルウェアを通していますが、ほとんどが セッションにまつわるミドルウェアで web ミドルウェアグループで定義されているミドルウェアとほぼ同じです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Laravel/Sanctum/Http/Middleware/EnsureFrontendRequestsAreStateful.php" data-lang="PHP" data-line="13,32"><code>/**
 * Handle the incoming requests.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  callable  $next
 * @return \Illuminate\Http\Response
 */
public function handle($request, $next)
{
    $this-&gt;configureSecureCookieSessions();

    return (new Pipeline(app()))-&gt;send($request)-&gt;through(
        static::fromFrontend($request) ? $this-&gt;frontendMiddleware() : []
    )-&gt;then(function ($request) use ($next) {
        return $next($request);
    });
}

(省略)

/**
 * Get the middleware that should be applied to requests from the &quot;frontend&quot;.
 *
 * @return array
 */
protected function frontendMiddleware()
{
    $middleware = array_values(array_filter(array_unique([
        config(&#39;sanctum.middleware.encrypt_cookies&#39;, \Illuminate\Cookie\Middleware\EncryptCookies::class),
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        config(&#39;sanctum.middleware.validate_csrf_token&#39;, config(&#39;sanctum.middleware.verify_csrf_token&#39;, \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class)),
        config(&#39;sanctum.middleware.authenticate_session&#39;),
    ])));

    array_unshift($middleware, function ($request, $next) {
        $request-&gt;attributes-&gt;set(&#39;sanctum&#39;, true);

        return $next($request);
    });

    return $middleware;
}</code></pre></div>



<p>その中から今回見ていくのは、VerifyCsrfToken になります。</p>



<h2 class="wp-block-heading">VerifyCsrfToken</h2>



<p>では、VerifyCsrfToken を見ていきます。</p>



<h3 class="wp-block-heading">CSRFトークンの検証</h3>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php" data-lang="PHP" data-line="13,14,15,16"><code>/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 *
 * @throws \Illuminate\Session\TokenMismatchException
 */
public function handle($request, Closure $next)
{
    if (
        $this-&gt;isReading($request) ||
        $this-&gt;runningUnitTests() ||
        $this-&gt;inExceptArray($request) ||
        $this-&gt;tokensMatch($request)
    ) {
        return tap($next($request), function ($response) use ($request) {
            if ($this-&gt;shouldAddXsrfTokenCookie()) {
                $this-&gt;addCookieToResponse($request, $response);
            }
        });
    }

    throw new TokenMismatchException(&#39;CSRF token mismatch.&#39;);
}</code></pre></div>



<p>VerifyCsrfToken では、まず検証の対象外かどうかを判定するために以下を走査しています。</p>



<ul class="wp-block-list">
<li>HTTP リクエストのメソッドが HEAD, GET, OPTIONS のいずれかかどうか</li>



<li>コンソールで実行されているかどうか
<ul class="wp-block-list">
<li><code>APP_RUNNING_IN_CONSOLE</code></li>



<li>SAPI が cli または phpdbg</li>
</ul>
</li>



<li>ユニットテストで実行されているかどうか
<ul class="wp-block-list">
<li><code>APP_ENV</code> が testing</li>
</ul>
</li>



<li>除外対象 URI かどうか
<ul class="wp-block-list">
<li><a href="https://laravel.com/docs/11.x/csrf#csrf-excluding-uris">Excluding URIs From CSRF Protection</a></li>
</ul>
</li>
</ul>



<p>検証の対象だった場合は、リクエストされた CSRF トークンの整合性を検証します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php" data-lang="PHP"><code>/**
 * Determine if the session and input CSRF tokens match.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function tokensMatch($request)
{
    $token = $this-&gt;getTokenFromRequest($request);

    return is_string($request-&gt;session()-&gt;token()) &&
            is_string($token) &&
            hash_equals($request-&gt;session()-&gt;token(), $token);
}</code></pre></div>



<p>ここでは、まずリクエストからトークンを取得します。フォームからのリクエストなどでボディに、<code>_token</code> があればそれを利用し、なければヘッダから <code>X-CSRF-TOKEN</code> または <code>X-XSRF-TOKEN</code> を取得します。<br>そして、そのトークンとサーバー側のセッションストレージに保存されているトークンを比較しています。この検証に失敗すると、TokenMismatchException が投げられ、ステータスコード 419 でレスポンスが返される、ということになります。</p>



<h3 class="wp-block-heading">CSRFトークンの付与</h3>



<p>CSRF トークンは Set-Cookie レスポンスヘッダで付与されます。これも VerifyCsrfToken でやっています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php" data-lang="PHP" data-line="20,45,62"><code>/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 *
 * @throws \Illuminate\Session\TokenMismatchException
 */
public function handle($request, Closure $next)
{
    if (
        $this-&gt;isReading($request) ||
        $this-&gt;runningUnitTests() ||
        $this-&gt;inExceptArray($request) ||
        $this-&gt;tokensMatch($request)
    ) {
        return tap($next($request), function ($response) use ($request) {
            if ($this-&gt;shouldAddXsrfTokenCookie()) {
                $this-&gt;addCookieToResponse($request, $response);
            }
        });
    }

    throw new TokenMismatchException(&#39;CSRF token mismatch.&#39;);
}

(省略)

/**
 * Add the CSRF token to the response cookies.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Symfony\Component\HttpFoundation\Response  $response
 * @return \Symfony\Component\HttpFoundation\Response
 */
protected function addCookieToResponse($request, $response)
{
    $config = config(&#39;session&#39;);

    if ($response instanceof Responsable) {
        $response = $response-&gt;toResponse($request);
    }

    $response-&gt;headers-&gt;setCookie($this-&gt;newCookie($request, $config));

    return $response;
}

(省略)

/**
 * Create a new &quot;XSRF-TOKEN&quot; cookie that contains the CSRF token.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  array  $config
 * @return \Symfony\Component\HttpFoundation\Cookie
 */
protected function newCookie($request, $config)
{
    return new Cookie(
        &#39;XSRF-TOKEN&#39;,
        $request-&gt;session()-&gt;token(),
        $this-&gt;availableAt(60 * $config[&#39;lifetime&#39;]),
        $config[&#39;path&#39;],
        $config[&#39;domain&#39;],
        $config[&#39;secure&#39;],
        false,
        false,
        $config[&#39;same_site&#39;] ?? null,
        $config[&#39;partitioned&#39;] ?? false
    );
}
</code></pre></div>



<p>ここでは、<code>XSRF-TOKEN</code> でセッショントークンを返すようにしています。</p>



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



<ul class="wp-block-list">
<li>Laravel Sanctum インストールの手順を行うと、api ミドルウェアグループでセッションの検証ができるようになる</li>



<li>CSRF トークンの検証は、VerifyCsrfToken ミドルウェアで行っている
<ul class="wp-block-list">
<li>対象でなければ検証しない</li>



<li>対象であればトークンとセッションデータを比較する</li>
</ul>
</li>



<li>CSRF トークンは Set-Cookie レスポンスヘッダに <code>XSRF-TOKEN</code> を付与している</li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【WordPress】ホストネットワーキングモードでWP-Cronをちゃんと動かす</title>
		<link>https://wptech.kiichiro.work/793kyf6xrh/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Wed, 18 Sep 2024 15:46:05 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3256</guid>

					<description><![CDATA[先日、Docker Desktop でもホストネットワーキングモードがサポートされたようです。 Docker Desktop内のコンテナに対して「localhost」でアクセス可能に、WSL2のストレージ領域を自動で縮小 [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>先日、Docker Desktop でもホストネットワーキングモードがサポートされたようです。</p>




<a rel="noopener" href="https://www.publickey1.jp/blog/24/docker_desktoplocalhostwsl2docker_desktop_434.html" title="Docker Desktop内のコンテナに対して「localhost」でアクセス可能に、WSL2のストレージ領域を自動で縮小など新機能、Docker Desktop 4.34正式リリース" 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://www.publickey1.jp/2024/docker434ga01.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">Docker Desktop内のコンテナに対して「localhost」でアクセス可能に、WSL2のストレージ領域を自動で縮小など新機能、Docker Desktop 4.34正式リリース</div><div class="blogcard-snippet external-blogcard-snippet">Docker社は、WindowsやMac、Linuxに手軽にDockerコンテナ環境を導入し利用できるソフトウェアであるDocker Desktopの最新版「Docker Desktop 4.34」正式版のリリースを発表しました。 Dock...</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://www.publickey1.jp/blog/24/docker_desktoplocalhostwsl2docker_desktop_434.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">www.publickey1.jp</div></div></div></div></a>



<p>どのようなときにホストネットワーキングモードの恩恵を受けられるか考えていたのですが、ローカル環境かつ Nginx + PHP-FPM 構成で WP-Cron を使った時に良さそうだなと思ったのでやってみました。</p>



<h2 class="wp-block-heading">WP-Cron が機能しない問題</h2>



<p>ローカル環境で Nginx + PHP-FPM 構成で WordPress を動かそうとすると、WP-Cron が機能しなくなるというのは、誰もが一度は通った道かと思います。</p>



<p>WP-Cron が機能しなくなる理由を簡単に説明すると、以下のような流れになります。</p>



<ol class="wp-block-list">
<li><strong>WP-Cron は WordPress へのリクエストによってトリガーされる</strong><br>WP-Cron はページへのアクセスがあった際にスケジュールを確認し、必要があれば <code>site_url</code> 配下のエンドポイントへ HTTP リクエストを送信します。</li>



<li><strong>site_url がローカル環境では localhost になる</strong><br>ローカル環境で WordPress を実行しているため、<code>site_url</code> は <code>http://localhost</code> になることが多いです。そのため、WP-Cron は <code>localhost</code> に対してリクエストを送信することになります。</li>



<li><strong>PHP-FPM コンテナで localhost にリクエストをすると、自分自身に送信される</strong><br>Docker コンテナで <code>localhost</code> はコンテナ自身を示します。</li>



<li><strong>PHP-FPM コンテナには HTTP サーバーは開かれていない</strong><br>PHP-FPM コンテナは HTTP リクエストを受け付けるポート (通常は 80 番) が開かれていません。よって、リクエストが失敗し、WP-Cron が実行できなくなります。</li>
</ol>



<p>要するに、<code>localhost</code> が示す先がコンテナ自身になってしまうということが原因ですので、WordPress を実行するサーバーの IP アドレスにドメインが関連付けられている場合 (本番環境など) では発生し得ません。</p>



<p>WP-Cron が機能していないことは管理画面の通知から窺い知ることが出来ます。<a href="https://ja.wordpress.org/plugins/wp-crontrol/">WP Crontrol</a> をインストールした環境であれば設定画面で確認出来ます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="195" src="https://wptech.kiichiro.work/wp-content/uploads/2024/09/3369aed6f02f6b45398f6124399a98a1-1024x195.png" alt="WP Crontrol の通知で、WP-Cron が機能しないことを示す「cURL error 7: Failed to connect to localhost port 80 after 0 ms: Couldn't connect to server。」が表示されている" class="wp-image-3309" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/09/3369aed6f02f6b45398f6124399a98a1-1024x195.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/3369aed6f02f6b45398f6124399a98a1-300x57.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/3369aed6f02f6b45398f6124399a98a1-768x146.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/3369aed6f02f6b45398f6124399a98a1-1536x292.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/3369aed6f02f6b45398f6124399a98a1-2048x389.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>cURL error 7: Failed to connect to localhost port 80 after 0 ms: Couldn&#39;t connect to server。</code></pre></div>



<p>この事象の回避方法としては、HTTP リクエストを投げる先を <code>cron_request</code> フィルターフックで指定しているので、<code>host.docker.internal</code> などに書き換えるなどがあります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-php" data-file="cron.php" data-lang="PHP" data-line="4"><code>$cron_request = apply_filters(
	&#39;cron_request&#39;,
	array(
		&#39;url&#39;  =&gt; add_query_arg( &#39;doing_wp_cron&#39;, $doing_wp_cron, site_url( &#39;wp-cron.php&#39; ) ),
		&#39;key&#39;  =&gt; $doing_wp_cron,
		&#39;args&#39; =&gt; array(
			&#39;timeout&#39;   =&gt; 0.01,
			&#39;blocking&#39;  =&gt; false,
			/** This filter is documented in wp-includes/class-wp-http-streams.php */
			&#39;sslverify&#39; =&gt; apply_filters( &#39;https_local_ssl_verify&#39;, false ),
		),
	),
	$doing_wp_cron
);</code></pre></div>



<p>ただ、ローカル環境のみで発生する事象に対して手を加えるのは、若干の気持ち悪さが残ります。</p>



<p>Docker のホストネットワーキングモードを使用すれば、<code>localhost</code> はホストを指すことになるので、上記の問題が解消しそうです。</p>



<h2 class="wp-block-heading">やってみた</h2>



<p>まずは、ホストネットワーキングモードを Docker Desktop 側で有効化します。手順は以下を参考にしました。</p>




<a rel="noopener" href="https://docs.docker.com/engine/network/drivers/host/#docker-desktop" title="Host network driver" 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://docs.docker.com/images/thumbnail.webp" 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">Host network driver</div><div class="blogcard-snippet external-blogcard-snippet">All about exposing containers on the Docker host&#039;s network</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.docker.com/engine/network/drivers/host/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">docs.docker.com</div></div></div></div></a>



<p>次に、Docker で Nginx + PHP-FPM + WordPress の環境を作ってみます。compose.yaml は以下です。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="compose.yaml" data-line="11,21,27"><code>services:
   db:
     image: mysql:8.0
     volumes:
       - db_data:/var/lib/mysql
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
     network_mode: &quot;host&quot;

   nginx:
    image: nginx:1.27.1-bookworm
    volumes:
      - ./default.conf:/etc/nginx/conf.d/default.conf
    volumes_from:
      - wordpress-fpm
    depends_on:
      - wordpress-fpm
    network_mode: &quot;host&quot;

   wordpress-fpm:
     image: wordpress:6.6.2-php8.1-fpm
     depends_on:
       - db
     network_mode: &quot;host&quot;

volumes:
    db_data:
</code></pre></div>



<p>ここで <code>network_mode: "host"</code> を指定することでホストネットワーキングモードが利用できます。</p>



<p>Nginx の default.conf は以下を用意しました。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain" data-file="default.conf" data-line="23"><code>server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    root /var/www/html;
    index index.php;

    location / {
        index  index.php;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        fastcgi_pass   localhost:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}</code></pre></div>



<p>上記のファイルを作成出来たら <code>docker compose up</code> で立ち上げ、ブラウザから <code>localhost</code> にアクセスします。あとは適当にポチポチと設定をしてログインします。</p>



<p>データベースの設定での注意点として、ホスト名は <code>localhost</code> ではなく <code>127.0.0.1</code> を指定してください。</p>




<a rel="noopener" href="https://dev.mysql.com/doc/refman/8.0/ja/connecting.html" title="MySQL :: MySQL 8.0 &#12522;&#12501;&#12449;&#12524;&#12531;&#12473;&#12510;&#12491;&#12517;&#12450;&#12523; :: 4.2.4 &#12467;&#12510;&#12531;&#12489;&#12458;&#12503;&#12471;&#12519;&#12531;&#12434;&#20351;&#29992;&#12375;&#12383; MySQL Server &#12408;&#12398;&#25509;&#32154;" 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%2Fdev.mysql.com%2Fdoc%2Frefman%2F8.0%2Fja%2Fconnecting.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">MySQL :: MySQL 8.0 &#12522;&#12501;&#12449;&#12524;&#12531;&#12473;&#12510;&#12491;&#12517;&#12450;&#12523; :: 4.2.4 &#12467;&#12510;&#12531;&#12489;&#12458;&#12503;&#12471;&#12519;&#12531;&#12434;&#20351;&#29992;&#12375;&#12383; MySQL Server &#12408;&#12398;&#25509;&#32154;</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.mysql.com/doc/refman/8.0/ja/connecting.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">dev.mysql.com</div></div></div></div></a>



<p>ログインしてダッシュボードまで辿り着いたら、WP-Cron の確認をします。WP Crontrol をインストールして設定画面を見てみると「cURL error 7: ~」のメッセージが表示されていないことが確認出来ます。</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="173" data-id="3336" src="https://wptech.kiichiro.work/wp-content/uploads/2024/09/9bee04000217b9bc26441371a8ca5acd-1024x173.png" alt="WP Crontrol の Cron イベント画面でエラー通知が表示されていない" class="wp-image-3336" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/09/9bee04000217b9bc26441371a8ca5acd-1024x173.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/9bee04000217b9bc26441371a8ca5acd-300x51.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/9bee04000217b9bc26441371a8ca5acd-768x130.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/9bee04000217b9bc26441371a8ca5acd-1536x260.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/9bee04000217b9bc26441371a8ca5acd-2048x347.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>
</figure>



<p>一覧から適当なイベントの「今すぐ実行」をクリックすると通知が表示され、正常に実行されたことが確認出来ます。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="187" src="https://wptech.kiichiro.work/wp-content/uploads/2024/09/6f9e18e0d3dee9917f96a7c456ae717b-1024x187.png" alt="WP Crontrol の Cron イベント画面で正常終了した通知メッセージが表示されている" class="wp-image-3337" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/09/6f9e18e0d3dee9917f96a7c456ae717b-1024x187.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/6f9e18e0d3dee9917f96a7c456ae717b-300x55.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/6f9e18e0d3dee9917f96a7c456ae717b-768x141.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/6f9e18e0d3dee9917f96a7c456ae717b-1536x281.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2024/09/6f9e18e0d3dee9917f96a7c456ae717b-2048x375.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">まとめと感想</h2>



<p>Docker Desktop でホストネットワーキングモードがサポートされたと聞いたときに真っ先にこのネタが浮かびましたが、一般的には安定感の無い WP-Cron をここまでして使いたいモチベーションは無いだろうなとも思いました。個人的には長年抱え続けてきた気持ち悪さが解決したので満足度は高いです。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Lambda Web AdapterでRemixアプリケーションをサーバーレス化してCDKでデプロイする</title>
		<link>https://wptech.kiichiro.work/617bivwxur/</link>
		
		<dc:creator><![CDATA[むらおか]]></dc:creator>
		<pubDate>Thu, 11 Jul 2024 09:08:50 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[Lambda]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Remix]]></category>
		<guid isPermaLink="false">https://wptech.kiichiro.work/?p=3178</guid>

					<description><![CDATA[既存の Web フレームワークを Lambda に組み込む方法として Lambda Web Adapter というのがあります。 Lambda Web Adapter でウェブアプリを (ほぼ) そのままサーバーレス化す [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>既存の Web フレームワークを Lambda に組み込む方法として Lambda Web Adapter というのがあります。</p>




<a rel="noopener" href="https://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter" title="Lambda Web Adapter でウェブアプリを (ほぼ) そのままサーバーレス化する (2025 年改訂版)  - 変化を求めるデベロッパーを応援するウェブマガジン | AWS" 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://d1.awsstatic.com/Developer%20Marketing/jp/magazine/2025/202504/thumb_lambda-web-adapter_2025.eaa3dee4029c2885d864b4da103ab18cd6b2b4f9.jpg" 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">Lambda Web Adapter でウェブアプリを (ほぼ) そのままサーバーレス化する (2025 年改訂版)  - 変化を求めるデベロッパーを応援するウェブマガジン | AWS</div><div class="blogcard-snippet external-blogcard-snippet">VM やコンテナ用に実装されたウェブアプリを、ほとんどそのまま Lambda でも動かせる AWS Lambda Web Adapter について、新しい実装パターンを含めた使い方、仕組みや対応する Web フレームワーク、性能をご紹介しま...</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://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter/" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">aws.amazon.com</div></div></div></div></a>




<a rel="noopener" href="https://github.com/awslabs/aws-lambda-web-adapter" title="GitHub - awslabs/aws-lambda-web-adapter: Run web applications on AWS Lambda" 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://opengraph.githubassets.com/d477a0d6d3e245d785d915f1b8b645c121d6d8cd3e618243358d4ae6e1b114cb/awslabs/aws-lambda-web-adapter" 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">GitHub - awslabs/aws-lambda-web-adapter: Run web applications on AWS Lambda</div><div class="blogcard-snippet external-blogcard-snippet">Run web applications on AWS Lambda. Contribute to awslabs/aws-lambda-web-adapter development by creating an account on G...</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://github.com/awslabs/aws-lambda-web-adapter" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<p>その仕組みですが、API Gateway などの統合先から受信したイベントをフレームワークの手前にある Lambda Web Adapter のエントリポイントが HTTP リクエストに変換して、フレームワークに渡してくれる、というイメージで良さそうです。</p>



<p>公式の実装例では、Node.js であれば Next.js と Express.js が対応していますが、HTTP であればどのようなフレームワークも動作するはずです。</p>



<p>今回はこれを使って Remix アプリをサーバーレス化してみました。既に作ったものは GitHub にあげてあります。</p>




<a rel="noopener" href="https://github.com/ShotaroMuraoka/cdk-serverless-remix" title="GitHub - ShotaroMuraoka/cdk-serverless-remix: Remix アプリケーションを Lambda Web Adapter でサーバーレス化した CDK" 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://opengraph.githubassets.com/7e2f8909af7deb175a15b54c16dbbb4c74009bec87bc2f528dea48788888602f/ShotaroMuraoka/cdk-serverless-remix" 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">GitHub - ShotaroMuraoka/cdk-serverless-remix: Remix アプリケーションを Lambda Web Adapter でサーバーレス化した CDK</div><div class="blogcard-snippet external-blogcard-snippet">Remix アプリケーションを Lambda Web Adapter でサーバーレス化した CDK. Contribute to ShotaroMuraoka/cdk-serverless-remix development by crea...</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://github.com/ShotaroMuraoka/cdk-serverless-remix" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<h2 class="wp-block-heading">CDKの準備</h2>



<p>デプロイの方法は CDK にしました。init で新しいプロジェクトを作成しておきます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ cdk --version
2.148.0 (build e5740c0)

$ cdk init app --language typescript</code></pre></div>



<p>今回は HTTP リクエストを受け付けるので、API Gateway と統合します。Stack は以下からお借りしました。</p>



<figure class="wp-block-embed is-type-rich is-provider-hatena-blog wp-block-embed-hatena-blog"><div class="wp-block-embed__wrapper">
<iframe title="AWS Lambda Web AdapterでServerless Next.jsを実現する  - Activ8 Tech Blog" src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fsynamon.hatenablog.com%2Fentry%2F2023%2F07%2F18%2F080000" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;"></iframe>
</div></figure>



<div class="hcb_wrap"><pre class="prism line-numbers lang-ts" data-file="my-lambda-stack.ts" data-lang="TypeScript"><code>import * as cdk from &#39;aws-cdk-lib&#39;;
import { Construct } from &#39;constructs&#39;;
import * as lambda from &#39;aws-cdk-lib/aws-lambda&#39;;
import { Platform } from &#39;aws-cdk-lib/aws-ecr-assets&#39;;
import * as apigw from &#39;aws-cdk-lib/aws-apigatewayv2&#39;;
import { HttpLambdaIntegration } from &#39;aws-cdk-lib/aws-apigatewayv2-integrations&#39;;

export class MyLambdaStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    const handler = new lambda.DockerImageFunction(this, &#39;Handler&#39;, {
      code: lambda.DockerImageCode.fromImageAsset(&#39;./my-remix&#39;, { // Remix アプリケーションのディレクトリ名とする
        platform: Platform.LINUX_AMD64,
      }),
      memorySize: 256,
      timeout: cdk.Duration.seconds(30),
    });

    new apigw.HttpApi(this, &#39;Api&#39;, {
      apiName: &#39;MyLambdaRemix&#39;,
      defaultIntegration: new HttpLambdaIntegration(&#39;Integration&#39;, handler),
    });
  }
}
</code></pre></div>



<h2 class="wp-block-heading">Remixアプリケーションの作成</h2>



<p>CDK のプロジェクトルートで Remix アプリを作成します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ npx create-remix@latest</code></pre></div>



<p>Remix アプリのルートに Dockerfile を作成します。Remix に対応した Dockerfile を作成したことがなかったので、以下を参考にそれっぽく書いてみました。</p>




<a rel="noopener" href="https://github.com/clintonwoo/hackernews-remix-react/blob/main/Dockerfile" title="hackernews-remix-react/Dockerfile at main · clintonwoo/hackernews-remix-react" 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://opengraph.githubassets.com/9f4b585a4145808d862d74acfde120c24aa19eb58f0d39f34c2549a48ecc5e13/clintonwoo/hackernews-remix-react" 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">hackernews-remix-react/Dockerfile at main · clintonwoo/hackernews-remix-react</div><div class="blogcard-snippet external-blogcard-snippet">Hacker News clone written with universal TypeScript, using React and Remix. - clintonwoo/hackernews-remix-react</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://github.com/clintonwoo/hackernews-remix-react/blob/main/Dockerfile" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">github.com</div></div></div></div></a>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash" data-line="23, 40"><code>FROM node:22-bookworm-slim AS base

FROM base AS deps
RUN mkdir /app
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci

FROM base AS builder

RUN mkdir /app
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NODE_ENV production
RUN npm run build

FROM base AS runner

# lambda-web-adapter
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.5.0 /lambda-adapter /opt/extensions/lambda-adapter

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 remix

RUN mkdir /app
WORKDIR /app

RUN chown remix:nodejs ./
USER remix

COPY --from=builder --chown=remix:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=remix:nodejs /app/build ./build
COPY --from=builder --chown=remix:nodejs /app/public ./public
COPY --from=builder --chown=remix:nodejs /app/package.json /app/package-lock.json ./

ENV NODE_ENV production
ENV PORT 3000
EXPOSE 3000

CMD [&quot;npm&quot;, &quot;run&quot;, &quot;start&quot;]</code></pre></div>



<p>ここでは Lambda Web Adapter のために難しい記述をする必要はありません。ローカルでも ECS でも動くコンテナイメージであれば、Lambda Web Adapter で動作するようです。</p>



<p>追記が必要なのは Lambda の extension を追加するというところのみです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.5.0 /lambda-adapter /opt/extensions/lambda-adapter</code></pre></div>



<p>また、デフォルトでは 8080 でポートを Listen しているので、変更する場合は PORT を指定します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-lang="Bash"><code>ENV PORT 3000</code></pre></div>



<h2 class="wp-block-heading">デプロイ</h2>



<p>CDK のルートディレクトリでデプロイを実行します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-plain"><code>$ cdk deploy</code></pre></div>



<p>管理コンソールから API Gateway の画面を確認すると、作成した API Gateway のエンドポイントが作成されています。</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="384" src="https://wptech.kiichiro.work/wp-content/uploads/2024/07/24ba3ff1ffab27bfd8a591fc1c9770c3-1024x384.png" alt="AWS の管理コンソールで API Gateway &gt; API &gt; MyLambdaRemixの画面を開いている。エンドポイントが有効になっており、リンクが表示されている" class="wp-image-3225" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/07/24ba3ff1ffab27bfd8a591fc1c9770c3-1024x384.png 1024w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/24ba3ff1ffab27bfd8a591fc1c9770c3-300x112.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/24ba3ff1ffab27bfd8a591fc1c9770c3-768x288.png 768w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/24ba3ff1ffab27bfd8a591fc1c9770c3-1536x576.png 1536w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/24ba3ff1ffab27bfd8a591fc1c9770c3-2048x768.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>デフォルトのエンドポイントの URL をクリックして Remix アプリの画面が表示されていれば OK です。</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="952" height="502" src="https://wptech.kiichiro.work/wp-content/uploads/2024/07/5a7c608aaf71c8c8671576d79db1798d.png" alt="Remix の初期の画面で、Welcome to Remix と表示されている" class="wp-image-3228" srcset="https://wptech.kiichiro.work/wp-content/uploads/2024/07/5a7c608aaf71c8c8671576d79db1798d.png 952w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/5a7c608aaf71c8c8671576d79db1798d-300x158.png 300w, https://wptech.kiichiro.work/wp-content/uploads/2024/07/5a7c608aaf71c8c8671576d79db1798d-768x405.png 768w" sizes="(max-width: 952px) 100vw, 952px" /></figure>



<h2 class="wp-block-heading">参考資料</h2>




<a rel="noopener" href="https://serverless.co.jp/blog/g30vzpio0ww" title="Dockerを使わない、Remix / Next.js 14 など最新ウェブフレームワークのAWS完全サーバーレス構成と環境構築方法 | ブログ | Serverless Operations" 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://images.microcms-assets.io/assets/7c0b324145eb4ee6bd26d44022795cf4/219c94fc1d5b45038289ecdb40b09463/overview.png?w=1200&#038;fm=webp&#038;q=80" 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">Dockerを使わない、Remix / Next.js 14 など最新ウェブフレームワークのAWS完全サーバーレス構成と環境構築方法 | ブログ | Serverless Operations</div><div class="blogcard-snippet external-blogcard-snippet">（※ 2025/01/31 追記）この記事の後続編として、React Router v7 と Next.js 15 を利用する構成の詳細な構築手順について、こちらの記事（</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://serverless.co.jp/blog/g30vzpio0ww" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">serverless.co.jp</div></div></div></div></a>



<p></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
