Ponz Dev Log

ゆるい備忘録的なブログ

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] を許可したら通った。

docs.aws.amazon.com

{
  "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で実現する方法を備忘録として残します。

やりたいこと

  • クライアントはOAuth2で取得したアクセストークンを渡す。
  • アクセストークンで指定されたスコープでアクセス制御したい。
    • 特定のスコープを含む場合のみバックエンドのAPIトラフィックを流す。
    • 特定のスコープを含まない場合は、バックエンドのAPIへのトラフィックを流さずクライアントに403 Forbiddenを返す。

実現方式(この記事のモチベーション)

JavaScriptだけでなく他の選択肢も並べてみます。

  1. JWT認証認可モジュール "ngx_http_auth_jwt_module" を入れる ... 有償版のNginx Plusの機能 ref: Setting up JWT Authentication | NGINX Documentation
  2. Luaスクリプトを自分で書く
  3. JavaScritpモジュール "ngx_http_js_module" を入れてJavaScriptを自分で書く
  4. 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.org

前提

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系には最初から入っています。

https://github.com/nginx/docker-nginx/blob/8852665dbc86d516617450cf6117786a93f37bea/stable/alpine/Dockerfile#L11-L18

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リポジトリにあったので参考にして書いてみます。

github.com

認可処理の実装

プレーンな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_passjs_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に出力されない場合がありました。

docs.aws.amazon.com

自分の場合は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 ではなくUbuntuAmazon Linux 2の例でした。

dev.classmethod.jp

iselegant.hatenablog.com


以上。

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とは?

github.com

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を使っている場合は podmandocker と読み替えてください。

$ 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では使用できません。とりあえずセットしておくとよいでしょう。

dev.classmethod.jp

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 というファイルを作ると設定をマージしてくれます。

developer.hashicorp.com

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 -
...

以上。

Open Libertyをオフライン環境でもZIPファイルから起動する

タイトルの記事をQiitaに投稿しました。 やんごとなき理由でMaven Centralなどにアクセスできないオフライン環境で開発するシーン向けです。 Liberty Maven pluginのおかげでZIPファイルさえあればオンライン環境と同じ開発体験が得られるのはいいですね。

qiita.com


以上。

Open Libertyで管理するDatasourceをSpring BootからJNDI参照する

タイトルの記事をQiitaに投稿しました。

データベースの接続情報やコネクションプールの設定をアプリサーバー側で管理している状況で、Spring Bootからアプリサーバーで持っているDataSourceを使いたかったのでので備忘録でした。Javaのコードは書かずSpring Bootのプロパティ1行で実現できちゃうのは便利ですね。

qiita.com


以上。