Amazon Linux 2023にEclipse Temurin JDKを入れる
Amazon Linux 2023にEclipse Temurin JDKをインストールする必要があったので手順を残す。
TemurinはAmazon Linux 2023を公式のサポートの一覧に含めていないので、実ワークロードで利用する場合は十分テストを行なうこと。
Eclipse Temurin Supported Platform
https://adoptium.net/ja/supported-platforms
環境情報
| 項目 | バージョン・設定 |
|---|---|
| Amazon Linuxバージョン | Amazon Linux 2023.11.20260514 |
| EC2インスタンスクラス | t3.micro |
| CPUアーキテクチャ | x86_64 |
$ cat /etc/os-release NAME="Amazon Linux" VERSION="2023" ID="amzn" ID_LIKE="fedora" VERSION_ID="2023" PLATFORM_ID="platform:al2023" PRETTY_NAME="Amazon Linux 2023.11.20260514" ANSI_COLOR="0;33" CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023" HOME_URL="https://aws.amazon.com/linux/amazon-linux-2023/" DOCUMENTATION_URL="https://docs.aws.amazon.com/linux/" SUPPORT_URL="https://aws.amazon.com/premiumsupport/" BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023" VENDOR_NAME="AWS" VENDOR_URL="https://aws.amazon.com/" SUPPORT_END="2029-06-30"
手順
Amazon Linux 2023はFedoraと近しい存在であるため、Fedoraにインストールする手順を元にする。 インストール方法はTARファイルを展開する方法と、RPMパッケージを使用する方法の2つある。 この記事では簡単にインストール・更新できるように後者のRPMパッケージからJDKを導入する。
公式の「CentOS/RHEL/Fedora の手順」に沿って進める。 Linux(RPM/DEB/APK)インストーラーパッケージ | Adoptium
1. RPMリポジトリをインスタンスに追加
公式の手順だと /etc/os-release からLinuxディストリビューション名を取得するリポジトリ設定ファイルを書くが、このままだとRPMリポジトリが見つからない。
$ sudo dnf search temurin Adoptium Error: Failed to download metadata for repo 'Adoptium': Cannot prepare internal mirrorlist
そこでAmazon Linux 2用のリポジトリパスを参照させる。
AIに聞くとRHEL用のリポジトリを参照させる提案も出てくるが、公式のIssueではAmazon Linux 2向けの設定を流用するようコメントがあったので今回はこちらを採用。
/etc/yum.repos.d/adoptium.repo に以下のリポジトリ設定を配置する。
$basearch の部分は自動的に x86_64 などに置き換えてくれるのでそのままでOK。
[Adoptium] name=Adoptium baseurl=https://packages.adoptium.net/artifactory/rpm/amazonlinux/2/$basearch enabled=1 gpgcheck=1 gpgkey=https://packages.adoptium.net/artifactory/api/gpg/key/public
2. 利用可能なバージョンを検索
1の設定を配置後にEclipse Temurin JDK 25を検索してみると利用可能なものがいくつか取得できた。 JDKだけでなく、実行時のみ必要なランタイム環境のみのJREや、GUI系のライブラリを除外したヘッドレスJREもある。
$ sudo dnf search temurin-25 Last metadata expiration check: 0:00:19 ago on Sat May 23 12:09:49 2026. ============================================= Name Matched: temurin-25 ============================================== temurin-25-jdk.x86_64 : Eclipse Temurin 25 JDK temurin-25-jre.x86_64 : Eclipse Temurin 25 JRE temurin-25-jre-headless.x86_64 : Eclipse Temurin 25 JRE
3. 目当てのバージョンでJDKをインストール
Eclipse Temurin JDK 25をインストールしてみる。
$ sudo dnf install -y temurin-25-jdk Last metadata expiration check: 0:02:51 ago on Sat May 23 12:09:49 2026. Dependencies resolved. ===================================================================================================================== Package Architecture Version Repository Size ===================================================================================================================== Installing: temurin-25-jdk x86_64 25.0.3.0.0.9-0 Adoptium 100 M Installing dependencies: ... Complete!
4. インストール後の検証
パスが通った状態で java コマンドを実行できる状態になった。
実態はシンボリックリンクをいくつか経由した先となっている。
$ which java /usr/bin/java $ ls -l /usr/bin/java lrwxrwxrwx. 1 root root 22 May 23 12:13 /usr/bin/java -> /etc/alternatives/java $ ls -l /etc/alternatives/java lrwxrwxrwx. 1 root root 41 May 23 12:13 /etc/alternatives/java -> /usr/lib/jvm/java-25-temurin-jdk/bin/java $ java -version openjdk version "25.0.3" 2026-04-21 LTS OpenJDK Runtime Environment Temurin-25.0.3+9 (build 25.0.3+9-LTS) OpenJDK 64-Bit Server VM Temurin-25.0.3+9 (build 25.0.3+9-LTS, mixed mode, sharing)
以上。
2025年近況とまとめ
ここ数ヶ月あまり記事を書けなかったので近況報告込みで今年をざっくりまとめます。
仕事
昨年に引き続き同じクライアントとシステムエンジニアとしてのお仕事を継続。所属組織のミッション的には色んなところに顔出して新しい技術や方法論を試すことが多いのですが、単一のクライアントに年単位でどっぷり入り込むことは久しぶりでした。
1月から9月までマイクロサービスの共通基盤の構築と、基盤利用の伴走、各マイクロサービスの非機能要件設計やテストのレビューなどやっていました。 詳しくは書けないけれど、技術的にはAWS、Terraform、Ansible、GitHub Actionsを使ってた。インフラからミドルウェアを扱うことが多め。これらで足りない部分はJavaScriptでちょっとしたアプリを書いてました。 基盤系の仕事はクライアントに引き継ぎして自分の手からは離れたけれど、継続的に拡張・運用されているらしいので一安心。
GitHub CI/CD実践ガイドがすごく役に立ってました。初めてGitHub Actionsを本格的に使ったのですが、自分でComposite Actionを書くところまでできるようになりました。オススメの書籍です。 書籍リンク: https://gihyo.jp/book/2024/978-4-297-14173-8
10月以降は同じクライアントの別チームが炎上していたので立て直しのために急遽プロジェクトマネージャーとして入ってました。これがかなりの激務で体重が2kg以上落ちてた。 なんとか最初の1ヶ月でなあなあになっていたチーム内の役割分担と開発フローを整え、次の1ヶ月までかけて散らかっていた課題チケットを優先順位づけしてやる/やらないの交渉の連続だった。設計開発テストのレビューもやってる。地味だけどやらないことを決めるの大事。
プライベート
妻ちゃんと秋に富山県に旅行行きました。 激務に追われる前に行っちゃおうと2泊3日。 黒部宇奈月温泉、富山市内、射水市新湊と結構回った。海鮮がマジで美味い。甘エビは本当に甘いというか、旨味が詰まってた。水も美味しいから米もうまい。感動。 黒ラーメンも食べたけどあまりにも塩辛くて美味しいけどスープを飲めないのは初めてだった。白飯のおかずですね。 写真は富山駅近くで食べた黒ラーメンです。

2026年にむけて
2025年終盤は仕事ばかりで自分自身のメンテナンスが全くできてなかったので、2026年は意識して休むようにしたい。
仕事と興味に関するところだと、AIの波に乗り遅れているからAIエージェントを使う・作る両方をキャッチアップするところから始めたいですね。 ちなみに2026年1月付で肩書きが正式にアーキテクトになります。(それまではITスペシャリスト職というものだった、昇進です) 社会人になってもうすぐ丸10年。肩書きは変わっても所属や仕事は変わらないけど、来年も頑張ります。
以上。
AWS OrganizationsのSCPで利用可能リージョンを絞ったらAmazon Bedrockの呼び出しに失敗した話
タイトルどおり。
課題
個人で管理しているAWS Organizations配下のAWSアカウントでAmazon BedrocksのAPIを呼び出したら以下のエラーが出てうまく呼び出せなかった。Amazon BedrocksのAPIはStrands Agents SDKから呼び出した。基盤モデルはAnthropicのClaude Sonnect 4である。
botocore.errorfactory.AccessDeniedException: An error occurred (AccessDeniedException) when calling the ConverseStream operation: User: arn:aws:sts::************:assumed-role/AWSReservedSSO_***_***/Demo_user is not authorized to perform: bedrock:InvokeModelWithResponseStream on resource: arn:aws:bedrock:ap-northeast-2::foundation-model/anthropic.claude-sonnet-4-20250514-v1:0 with an explicit deny in a service control policy └ Bedrock region: ap-northeast-1 └ Model id: apac.anthropic.claude-sonnet-4-20250514-v1:0
呼び出し元は手元で東京リージョン (ap-northeast-1) を指定しているが、呼び出したエンドポイントはクロスリージョン推論プロファイルのものなので apac というプレフィックスがついている。
クロスリージョンと名前のとおり ap-northeast-1 がリソース名についていないのは想定どおりだが、ap-northeast-2 もSCPで許可しているのに呼び出しが失敗する。
SCPは以下のように Condition 句で利用可能なリージョンを制限するように設定している。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyAllOutsideAPAC", "Effect": "Deny", "NotAction": [ "iam:*", "cloudfront:*", "route53:*", "support:*", "aws-portal:*", "budgets:*", "health:*" ], "Resource": "*", "Condition": { "StringNotEquals": { "aws:RequestedRegion": [ "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "us-east-1", "us-west-2" ] } } } ] }
解決策
APACの全てのリージョンを許可する必要があった。 送信元リージョン単位に推論リクエスト送信先のリージョン一覧が公式ドキュメント(以下リンク先)に載っているので、 ap-northeast-1 に対応するリージョンを探せばよい。 今回のケースだと不足してた ap-south-[1|2], ap-southeast-[1|2|4] を許可したら通った。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyAllOutsideAPAC", "Effect": "Deny", "NotAction": [ ... ], "Resource": "*", "Condition": { "StringNotEquals": { "aws:RequestedRegion": [ "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-south-1", "ap-south-2", "ap-southeast-1", "ap-southeast-2", "ap-southeast-4", "us-east-1", "us-west-2" ] } } } ] }
以上。
JavaScriptでnginxのOAuth2認可処理を書く
nginxコミュニティ版をリバプロとして使うときに、バックエンドにトラフィックを流す前にOAuth2のアクセストークンの中身を参照して認可処理を挟みたい。 Luaのスクリプトを書けば細かいカスタマイズはできそうですが、もう少し慣れたJavaScriptで実現する方法を備忘録として残します。
やりたいこと
実現方式(この記事のモチベーション)
JavaScriptだけでなく他の選択肢も並べてみます。
- JWT認証認可モジュール "ngx_http_auth_jwt_module" を入れる ... 有償版のNginx Plusの機能 ref: Setting up JWT Authentication | NGINX Documentation
- Luaスクリプトを自分で書く
- JavaScritpモジュール "ngx_http_js_module" を入れてJavaScriptを自分で書く
- JWT認証認可機能が入った別のリバプロに切り替える
nginxはコミュニティ版を使用しているので1と4をまず除外。 そうなると自分で認可処理を書かないといけないので、慣れている3.JavaScriptで書くことにしました。
やり方
njs概要
nginx でJavaScriptを扱うモジュールは "ngx_http_js_module" (以下、njs) です。 nginxの多種多様な処理をJavaScriptで実現するためのモジュールです。 njs というモジュール略称と同名の組み込みJavaScriptエンジンでJavaScriptを処理します。 njsはECMAScript 5.1と6の一部と互換性があります。 JavaScriptエンジンは他にもモジュール組み込みのQuickJSにも差し替え可能で、ES2023まで互換性範囲を広げて扱えます。この記事ではJavaScriptエンジンにnjsを使用します。
前提
nginxをリバースプロキシとしてローカルのコンテナをたて、バックエンドに httpbin.org を使用しました。 また、JWTアクセストークンを発行するIDプロバイダーはローカルのKeycloakコンテナを使用します。 使用したコンテナのバージョンは次のとおりです。
| ソフトウェア | バージョン (コンテナイメージ) | 起動ポート |
|---|---|---|
| nginx | docker.io/library/nginx:1.28-alpine |
8081 |
| Keycloak (IdP) | quay.io/keycloak/keycloak:26.3.2 |
8080 |
モジュールのインストール
nginxのモジュールのインストール作業を簡略化するためコンテナイメージを使います。 njsはnginxの公式のコンテナイメージの1.28系には最初から入っています。
RUN set -x \
&& apkArch="$(cat /etc/apk/arch)" \
&& nginxPackages=" \
nginx=${NGINX_VERSION}-r${PKG_RELEASE} \
nginx-module-xslt=${NGINX_VERSION}-r${DYNPKG_RELEASE} \
nginx-module-geoip=${NGINX_VERSION}-r${DYNPKG_RELEASE} \
nginx-module-image-filter=${NGINX_VERSION}-r${DYNPKG_RELEASE} \
nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${NJS_RELEASE} \
認可処理のJavaScript部分はサンプルがGitHubリポジトリにあったので参考にして書いてみます。
認可処理の実装
プレーンなJavaScriptで記述します。
サンプルは var を使っていたのですが、njsはES5,6と互換性があるので今どきの書き方に合わせて const にしました。
// nginx/njs/authz.js function jwt(data) { const parts = data.split('.').slice(0,2) .map(v=>Buffer.from(v, 'base64url').toString()) .map(JSON.parse); return { headers:parts[0], payload: parts[1] }; } function authorize(r) { // Authorizationヘッダーの "Bearer " 部分を除外してアクセストークンのみ取り出す const payload_scopes = jwt(r.headersIn.Authorization.slice(7)).payload.scope; // 読み取り権限 demo/read がなければ権限エラーとする if (!payload_scopes.split(' ').includes('demo/read')) { r.error('Forbidden: insufficient scope'); r.return(403); return; } r.return(200); } export default {authorize}
nginx設定
認証認可用のリダイレクト処理 auth_request と、内部リダイレクト用のパス定義 internal を駆使しながら設定を書きます。
proxy_pass と js_content は同一ブロックには書けなかったので、/secured-api/* にマッチしたリクエストを /auth に内部リダイレクトして認可処理を走らせて 2xx が返ってきたらバックエンドを通す方式とした。
# njs モジュールを読み込み load_module modules/ngx_http_js_module.so; events { } http { # 認可処理用のJavaScriptを読み込み js_path "/etc/nginx/njs/"; js_import authz.js; server { listen 80; location / { root /usr/share/nginx/html; index index.html index.htm; } location /healthz { return 200; } # 内部リダイレクト先の認可処理エンドポイント location /auth { internal; js_content authz.authorize; } # 保護されたエンドポイント location /secured-api { auth_request /auth; proxy_pass https://httpbin.org/anything; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Authorization ""; } } }
コンテナ実行
Docker/Podman Composeで必要なコンテナを実行します。
compose.yml
services: idp: container_name: idp image: quay.io/keycloak/keycloak:26.3.2 ports: - "8080:8080" command: - start-dev environment: - KC_BOOTSTRAP_ADMIN_USERNAME=admin - KC_BOOTSTRAP_ADMIN_PASSWORD=admin proxy: container_name: proxy image: public.ecr.aws/docker/library/nginx:1.28-alpine ports: - "8081:80" environment: - NGINX_ENTRYPOINT_QUIET_LOGS=1 volumes: - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/njs:/etc/nginx/njs
compose.yml を入力としてコンテナを起動します。
$ podman compose up -d ✔ Container idp Started ✔ Container proxy Started
nginxのコンテナのログにnjsが起動していることがわかります。
2025/08/03 03:28:02 [notice] 1#1: js vm init njs: ...
リクエスト実行
Keycloakのアカウントコンソール http://localhost:8080/admin/master/console を開いて事前にレルム、クライアントID、シークレット、スコープを作成しておく。方法はこの記事の範囲を超えるので割愛。
| 項目名 | 設定値 |
|---|---|
| レルム(realms) | demo |
| クライアントID | demo-client |
| クライアントシークレット | Keycloakで自動生成 |
| クライアントスコープ | demo/read (読み取り)、demo/write (書き込み) |
Keycloakのトークンエンドポイントからアクセストークンを取得する。 リクエストパラメータのscopeに読み取り権限 (demo/read) を指定することでアクセストークンにもscopeを含めるようにする。
ACCESS_TOKEN=$(curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
-d grant_type=client_credentials \
-d client_id=demo-client \
-d client_secret=***** \
-d 'scope=demo/read' \
http://localhost:8080/realms/demo/protocol/openid-connect/token \
| jq -r '.access_token')
アクセストークンのペイロードをデコードすると scope に demo/read が含まれている。
$ echo $ACCESS_TOKEN | awk -F'.' '{print $2}' | base64 --decode | jq -r '.scope'
profile demo/read email
アクセストークンを含めてnginx経由でバックエンドにAPIを投げてみる。
認可処理で許可したスコープ demo/read が含まれているのでこれは問題なく200 OKでレスポンスが返ってくる。
$ curl -v --oauth2-bearer ${ACCESS_TOKEN} http://localhost:8081/secured-api/hogehoge
...
< HTTP/1.1 200 OK
< Server: nginx
...
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "localhost",
"User-Agent": "curl/8.15.0",
"X-Amzn-Trace-Id": "Root=...
},
"json": null,
"method": "GET",
"origin": "10.20.33.45",
"url": "https://localhost/anything/hogehoge"
}
scopeに demo/read を含まない別のアクセストークンを指定してみると、設定どおり 403 Forbidden で権限エラーが返されました。
$ curl -v --oauth2-bearer ${ACCESS_TOKEN} http://localhost:8081/secured-api/piyofuga
...
< HTTP/1.1 403 Forbidden
< Server: nginx
...
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
このときnginxコンテナのエラーログに error レベルでJavaScriptで定義した権限エラーのメッセージが出力されました。
2025/08/03 08:48:19 [error] 17#17: *1 js: Forbidden: insufficient scope
最終的なディレクトリ構造
$ tree .
.
├── compose.yml
└── nginx
├── config
│ └── nginx.conf
└── njs
└── authz.js
以上。
Amazon Linux 2023でECS Execのログを出力するために必要なパッケージをインストールする
Amazon ECSのコンテナを踏み台として使うのにECS Execを多用していたのですが、リンク先のとおりIAM権限を付与してもうまくログがS3やCloudWatch Logsに出力されない場合がありました。
自分の場合はAmazon Linux 2023のコンテナイメージをカスタマイズして使っていたわけだがドキュメントに記載のある script コマンドがが入っていなかった模様。
解決方法を備忘録として記録します。
コマンドログを Amazon S3 または CloudWatch Logs に正しくアップロードするには、コンテナイメージに script と cat をインストールする必要があります。
Amazon Linux 2023 ベースイメージの状態
本記事記載時点の最新イメージ Amazon Linux 2023.7.20250512 で必須コマンドが入っているか確認してみます。
$ podman run --rm -d --name al2023 public.ecr.aws/amazonlinux/amazonlinux:2023.7.20250512.0 sleep 999 9ec24a2a340b... $ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9ec24a2a340b public.ecr.aws/amazonlinux/amazonlinux:2023.7.20250512.0 sleep 999 6 seconds ago Up 6 seconds al2023 $ podman exec -it al2023 bash bash-5.2# dnf install -y which ... Complete! bash-5.2# bash-5.2# which cat /usr/bin/cat bash-5.2# which script /usr/bin/which: no script in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin)
cat は入っているが、script は入っていないことがわかります。
パッケージをコンテナイメージにインストール
script コマンドは util-linux パッケージをインストールすることで利用可能です。
Dockerfileでの定義例を以下に示します。
FROM public.ecr.aws/amazonlinux/amazonlinux:2023.7.20250512.0 RUN <<EOF # Common Utility dnf install -y which vim jq unzip gzip tar # 今回はこれを入れるべし dnf install -y util-linux # Cleanup dnf clean all EOF # 立ち上げたままにする ENTRYPOINT [ "tail", "-f", "/dev/null" ]
util-linuxインストール済みのコンテナイメージを立ち上げてみると、scriptコマンドも使えるようになっていました。 ログも無事にS3やCloudWatch Logsに送信できています。
bash-5.2# which script /usr/bin/script bash-5.2# script --version script from util-linux 2.37.4
参考情報
「ECS 踏み台」で調べると以下の記事がヒットしますが、Amazon Linux 2023 ではなくUbuntuやAmazon Linux 2の例でした。
以上。
TerraformとMotoでAWSリソースの作成をローカルで試そう
この記事は terraform Advent Calendar 2024 の8日目の記事です。 昨日は @mitomito さんによる [terraform]S3とTransferFamilyでsftpサーバを作るでした。
Table of Contents
- はじめに
- Motoとは?
- いざ実践
- 使用するソフトウェア
- Motoを起動
- Motoの動作確認
- Terraformの設定
- Terraformリソース定義の記述
- terraformコマンドを実行
はじめに
クラウドサービスのプロビジョニングにTerraformを使いたいものの、実環境に繋がずできるだけローカルで完結させたい場合ってありませんか? 例えば以下のようなケースです。
- クラウドのアカウントの調達リードタイム、コスト調整の関係でアカウント利用にハードルが高い。
- セキュリティポリシーの制約でインターネットの限られたサイトしか接続できない。
- 実際の環境を複数人で操作しているためテスト用途でも作業のバッティングが頻繁に発生する。
- Terraform初心者向けに手元で試してもらいたい。
今回はAWSにリソースを作成したいケースを例に、AWSのモックに使える Moto を使用して可能な限りローカルでTerraformの操作を完結させます。
Motoとは?
AWSサービスのモック機能を提供するPythonライブラリです。 Pythonを使わずともServer Modeで任意の環境で使用可能です。 コンテナイメージも提供されているのでこの記事ではコンテナでMotoを使用します。
MotoでサポートされたAWSリソースと操作は以下のサイトにまとめられています。 EC2、DynamoDB、SQSなど代表的なリソースはサポートされていますね。 Bedrockも操作は限定的ながらサポート対象に入っていることは驚きです。
https://docs.getmoto.org/en/stable/docs/services/index.html
いざ実践
使用するソフトウェア
| ソフトウェア | バージョン |
|---|---|
| AWS CLI | 2.22.12 |
| Terraform | 1.10.1 |
| Moto | 5.0.22 |
| Podman | 5.3.1 |
Motoを起動
コンテナイメージを手元にpullして podman コマンドでMotoを起動します。
Docker Desktopを使っている場合は podman を docker と読み替えてください。
$ podman pull ghcr.io/getmoto/motoserver:5.0.22
$ podman run --rm -d --name moto \
-p 3000:3000 \
-e MOTO_PORT=3000 \
-e MOTO_IAM_LOAD_MANAGED_POLICIES=true \
ghcr.io/getmoto/motoserver:5.0.22
Motoの公開ポートはデフォルトで5000ですが環境変数 MOTO_PORT で任意のポートに変更可能です。
また、IAMのAWS管理ポリシーを使用したい場合は環境変数 MOTO_IAM_LOAD_MANAGED_POLICIES をセットしないとMotoでは使用できません。とりあえずセットしておくとよいでしょう。
Motoの動作確認
簡易な動作確認でAWS CLIでMotoを呼び出します。
事前に環境変数 AWS_ENDPOINT をセットして aws コマンドを実行します。
TerraformでAWSサービスの呼び出しをMotoのエンドポイントに向けるためにも使用します。
$ export AWS_ENDPOINT_URL=http://localhost:3000
$ aws sts get-caller-identity --no-cli-pager
{
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:user/moto"
}
Terraformの設定
ローカルで動かす場合と実環境で実行する場合で設定を明示的に分けておくと便利です。
Terraformでは *_override.tf というファイルを作ると設定をマージしてくれます。
Terraformステートバックエンドの設定 (backend.tf) とTerraformプロバイダーの設定 (provider.tf) が既にある状態を仮定して、ローカルではMotoを使用するケースでそれぞれの設定を上書きするファイルを記述します。
バックエンド設定 デフォルト (backend.tf)
terraform { backend "s3" {} }
バックエンド設定 ローカル用の上書き (backend_override.tf)
terraform { backend "local" { path = "terraform.tfstate" } }
プロバイダー設定 デフォルト (provider.tf)
terraform { required_providers { aws = { source = "hashicorp/aws" version = "5.80.0" } } } provider "aws" { region = var.region }
プロバイダー設定 ローカル用の上書き (provider_override.tf)
MotoではサポートされていないAWS固有のチェック処理を外す設定 (skip_*) を記載します。
またAmazon S3へのアクセスは仮想ホスト形式が標準ですがローカルだと名前解決できないのでパス形式を強制する設定 (s3_use_path_style) も追加しておきます。Motoガイドでは同等設定の s3_force_path_style を指定するように記載がありますが、この設定はTerraform AWS Provider v5で削除されているので使用できません。
provider "aws" { skip_credentials_validation = true skip_metadata_api_check = true skip_requesting_account_id = true s3_use_path_style = true }
Terraformリソース定義の記述
Moto固有の設定を完了したら残りは通常のTerraformリソース定義の記述でOKです。 Amazon VPCを作成します。CIDRブロックをvariablesで受け取り、作成したVPCのIDをoutputsで出力もしてみましょう。
variables.tf
variable "region" { description = "AWS Region" type = string default = "ap-northeast-1" } variable "cidr_block" { description = "VPC CIDR Block" type = string default = "10.23.0.0/16" }
main.tf
resource "aws_vpc" "qiita" { cidr_block = var.cidr_block instance_tenancy = "default" enable_dns_hostnames = true enable_dns_support = true tags = { "Name" = "qiita-2024-vpc" } }
outputs.tf
output "vpc_id" { description = "VPC ID" value = aws_vpc.qiita.id }
terraformコマンドを実行
ローカルで terraform コマンドを実行してAmazon VPCを作成してみましょう。 Amazon VPCはMotoで標準的にサポートされているリソースなので問題なく実行完了します。
$ terraform init
Initializing the backend...
Successfully configured the backend "local"! Terraform will automatically
...
Terraform has been successfully initialized!
$ terraform validate
Success! The configuration is valid.
$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# aws_vpc.qiita will be created
+ resource "aws_vpc" "qiita" {
+ arn = (known after apply)
+ cidr_block = "10.23.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ enable_network_address_usage_metrics = (known after apply)
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags = {
+ "Name" = "qiita-2024-vpc"
}
+ tags_all = {
+ "Name" = "qiita-2024-vpc"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ vpc_id = (known after apply)
$ terraform apply -auto-approve
...
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
vpc_id = "vpc-14e084e497b5b9d80"
本当にMotoにもVPCが作成されているかAWS CLIでチェックしてみます。
Outputsに表示された vpc_id でクエリしてみると確かに指定したプロパティとタグ名が入っています。
$ aws ec2 describe-vpcs --vpc-ids vpc-14e084e497b5b9d80
{
"Vpcs": [
{
"OwnerId": "123456789012",
"InstanceTenancy": "default",
...(omit)...
"IsDefault": false,
"Tags": [
{
"Key": "Name",
"Value": "qiita-2024-vpc"
}
],
"VpcId": "vpc-14e084e497b5b9d80",
"State": "available",
"CidrBlock": "10.23.0.0/16",
"DhcpOptionsId": "default"
}
]
}
Motoの実行ログをのぞいてみると全てのAPIコールで「HTTP/1.1 200」が返却されていますね。 もし途中でエラーが発生した場合は未サポートのリソースや設定が含まれる場合があります。
$ podman logs moto podman logs moto WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:3000 * Running on http://10.88.0.2:3000 Press CTRL+C to quit 10.88.0.2 - - [07/Dec/2024 05:18:31] "POST / HTTP/1.1" 200 - 10.88.0.2 - - [07/Dec/2024 05:36:29] "POST / HTTP/1.1" 200 - 10.88.0.2 - - [07/Dec/2024 05:36:29] "POST / HTTP/1.1" 200 - ...
以上。