はじめに
formae という新しいIaCツールを知ったので、公式ドキュメントを見ながら実際に触ってみました。
この記事では、以下を解説します。
- formae の概要
- WSL2上でのインストール
- Quick Startで詰まったポイント
- AWSリソースの検出ができるか
結論から言うと、インストールとAWSリソース検出はできました。
一方で、公式ドキュメント通りに進めるといくつか詰まる箇所がありました。
formaeとは
2025年10月に発表された比較的新しいIaCツールです。
読み方は公式解説動画だと「フォーマ」と言っているように聞こえます。
Terraformで課題になりがちな、既存リソースの取り込みやドリフト検知・反映を意識して作られているように見えます。
公式ガイドを読む
公式ガイドを読んでいきます。
クイックスタートにも少し目を通しましたが、実行するコマンドは少なく、敷居は低そうです。
気になったポイントは以下です。
唯一の信頼できる情報源はコードです。コードは、すべてのインフラストラクチャリソースと変更を、完全にバージョン管理されたインフラストラクチャコードに統合します。
常に最新のインフラストラクチャコード:ツール外部で変更があった場合、それを検知します。変更を無視したり元に戻したりするのではなく、インフラストラクチャコードにマージします。これにより、重要な外部変更が失われることなく、インフラストラクチャコードに即座に反映されます。
公式ドキュメント上では、ツール外で発生した変更を検知し、インフラコード側へ取り込む思想のようです。
Terraformはコードを正とする思想のため、ここは異なります。
共存を前提に設計されています。移行やインポートは不要です。formaeはリソースを自動的に検出して更新し、他のIaCツールやインフラストラクチャ管理ツール、さらにはClickOpsとも問題なく共存します。
インポートなどをしなくてもリソースを自動的に検出してくれるようです。
完璧な検出ができるのならすごいツールだと思います。
terraformのインポートが使いづらいのでformaeの検出の精度が気になります。
クイックスタート
クイックスタートをやっていきます。
構成
Windows
┗WSL2
┗ubuntu
┗formae
手順
①インストール
クイックスタートに記載のコマンドでインストールを開始します。
user@host:~/formae$ /bin/bash -c "$(curl -fsSL https://hub.platform.engineering/get/formae.sh)"
Downloading: pelmgr
WARN privileged user required to manage path: /opt/pel
[sudo] password for user:
INFO refreshing: https://hub.platform.engineering/repos/platform.engineering/pel
<中略>
INFO installing auth-basic@0.1.0_20260513T053457Z
INFO installing ovh@0.1.4_20260512T200117Z
INFO installing standard@0.1.0_20260513T051415Z
IMPORTANT: ensure you add /opt/pel/bin to your PATH, and reload your shell configuration
user@host:~/formae$
WARN privileged user required to manage path: /opt/pel
インストール開始時に何か出てきたのでデフォルトでOKを押したらパスが変わったようです。
次に、パスの設定をします。
user@host:~/formae$ echo 'export PATH=/opt/pel/bin:$PATH' >> ~/.bashrc
source ~/.profile
user@host:~/formae$
user@host:~/formae$ formae --version
formae version: 0.85.2
go version: go1.26.2
user@host:~/formae$
これでインストールは完了です。
バージョンが表示されました。
また、ガイドに以下の記載があったのが気になりました。
Claude Codeをご利用ですか?自然な会話を通してインフラストラクチャを管理するには、formae MCPプラグインをインストールしてください。
AI時代に公開されたツールだけあり、MCPに対応しているようです。
是非とも試してみたいですがここではスルーします。
②最新化する
PATHの設定の時に参照したページに最新化のコマンドが記載されていました。
最新版にアップグレードしようとしたら、エラーになりました。
user@host:~/formae$ formae upgrade
Error: unknown command "upgrade" for "formae"
Usage: formae [OPTIONS] [COMMAND]
<中略>
Code: https://github.com/platform-engineering-labs/formae
Docs: https://docs.formae.io/en/latest
Bugs: https://github.com/platform-engineering-labs/formae/issues
user@host:~/formae$
upgradeではなく、updateなら通りました。バージョン差異でしょうか。
user@host:~/formae$ formae update
[sudo] password for user:
no updates available
user@host:~/formae$
③サンプルをワークスペースにコピーする
examplesディレクトリを探します。
user@host:~/formae$ ls /opt/pel/formae/examples/
ls: cannot access '/opt/pel/formae/examples/': No such file or directory
user@host:~/formae$ ls /opt/pel/formae/
plugins
user@host:~/formae$
examplesディレクトリがない。。
GitHubリポジトリを確認しましたが、examplesディレクトリは見つかりませんでした。
ここで公式のクイックスタート通りには進めなくなりました。
新しいツールなので仕方ない部分もありますが、クイックスタートで詰まるのは少し気になります。
仕方がないので、別のことを試していきます。
AWSからリソース情報を取得する
ハウツーガイドを参照しました。
最初のステップ自分のクラウドアカウントの中身を安全に確認したいからやってみるのがよさそうです。
インストールには成功したので、できるかもしれません。
AWSからの情報取得を試みます。
手順
事前準備
事前にAWS CLI導入とAWS configureの設定を行いました。※ここでは省略
aws sts get-caller-identityで情報が取得できれば問題ないです。
クラウドとの接続設定
ガイドに記載の通り、discovery-target.pklを作成します。
amends "@formae/forma.pkl"
import "@formae/formae.pkl"
import "@aws/aws.pkl"
forma {
new formae.Target {
label = "my-account"
discoverable = true // Enable discovery for this target
config = new aws.Config {
region = "ap-northeast-1"
}
}
}
applyしてみます。
user@host:~/formae/1.AwsImport$ formae apply --mode reconcile discovery-target.pkl
Error: –– Pkl Error ––
Cannot import dependency because there is no project found.
If you meant to import a path that starts with `@`, prefix the path with `./` (e.g. `import "./@myPath").
If you meant to import a dependency, ensure that this file is within a directory that contains a PklProject module.
1 | amends "@formae/forma.pkl"
^^^^^^^^^^^^^^^^^^^
at discovery-target (file:///home/user/formae/1.AwsImport/discovery-target.pkl)
user@host:~/formae/1.AwsImport$ cd /home/user/formae/1.AwsImport && pwd
エラーが出ました。
最初のステップというガイドをやっているはずですが、うまくいきません。
初期化formae project initを実行しましたが、ダメでした。
このエラーは、@formae や @aws の依存関係を解決できていないことが原因のようです。
PklProject に formae と aws の依存関係を明示的に追加しました。
PklProject
amends "pkl:Project"
dependencies {
["formae"] {
uri = "package://hub.platform.engineering/plugins/pkl/schema/pkl/formae/formae@0.85.2"
}
["aws"] {
uri = "package://hub.platform.engineering/plugins/aws/schema/pkl/aws/aws@0.1.10"
}
}
PklProject.deps.json
{
"schemaVersion": 1,
"resolvedDependencies": {
"package://hub.platform.engineering/plugins/pkl/schema/pkl/formae/formae@0": {
"type": "remote",
"uri": "projectpackage://hub.platform.engineering/plugins/pkl/schema/pkl/formae/formae@0.85.2",
"checksums": {
"sha256": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
},
"package://hub.platform.engineering/plugins/aws/schema/pkl/aws/aws@0": {
"type": "remote",
"uri": "projectpackage://hub.platform.engineering/plugins/aws/schema/pkl/aws/aws@0.1.10",
"checksums": {
"sha256": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
}
}
メッセージが変わりました。
user@host:~/formae/1.AwsImport$ formae apply --mode reconcile discovery-target.pkl
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
Error: error rendering error message: agent is not running; please start the agent and try again
Getting started: https://docs.formae.io/en/latest
user@host:~/formae/1.AwsImport$
Error: error rendering error message: agent is not running; please start the agent and try again
エージェントが起動していないので、起動させます。
user@host:~/formae/1.AwsImport$ formae agent start
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
2026-06-05T22:42:13+09:00 INF Starting agent id=XXXXXXXXXXXXXXXXXXXXXXX
2026-06-05T22:42:13+09:00 INF Started SQLite datastore filePath=/home/user/.pel/formae/data/formae.db
<中略>
2026-06-05T22:42:37+09:00 ERR (registrar) unable to read from socket with 127.0.0.1:45686: read tcp 127.0.0.1:4499->127.0.0.1:45686: i/o timeout [node=XXXXXXXX]
2026-06-05T22:42:41+09:00 ERR (registrar) proto version mismatch from 127.0.0.1:52058: 71 [node=XXXXXXXX]
2026-06-05T22:42:42+09:00 ERR (registrar) unable to read from socket with 127.0.0.1:52064: read tcp 127.0.0.1:4499->127.0.0.1:52064: i/o timeout [node=XXXXXXXX]
2026-06-05T22:42:37+09:00 ERR (registrar) unable to read from socket with 127.0.0.1:45686: read tcp 127.0.0.1:4499->127.0.0.1:45686: i/o timeout [node=XXXXXXXX]
2026-06-05T22:42:41+09:00 ERR (registrar) proto version mismatch from 127.0.0.1:52058: 71 [node=XXXXXXXX]
エラーが出ました。
proto version mismatch や i/o timeout のログは出ていましたが、agentプロセス自体は起動しているように見えました。
そのため、まずは別ターミナルからコマンドを実行して、agentが応答するかを確認しました。
user@host:~/formae/1.AwsImport$ formae apply --mode reconcile discovery-target.pkl
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
Command will
└── create target my-account
This operation will create 1 target(s).
Do you want to continue? (Y): Y
The asynchronous command has started on the formae agent.
Run the following command to check the status of this command:
formae status command --query='id:XXXXXXXXXXXXXXXXXXXXXXX'
user@host:~/formae/1.AwsImport$
通りました。
最終行に出てるコマンドを実行してみます。
user@host:~/formae/1.AwsImport$ formae status command --query='id:XXXXXXXXXXXXXXXXXXXXXXX'
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
┌─────────────────────────────┬─────────┬─────────┬────────┬──────┬──────────┬─────────┬───────┬──────┬──────────┬───────────────────┬──────┐
│ ID │ Command │ Status │ Change │ Wait │ Progress │ Success │ Retry │ Fail │ Canceled │ Started At │ Time │
├─────────────────────────────┼─────────┼─────────┼────────┼──────┼──────────┼─────────┼───────┼──────┼──────────┼───────────────────┼──────┤
│ XXXXXXXXXXXXXXXXXXXXX │ apply │ Success │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ 06/05/2026 1:46PM │ 0s │
└─────────────────────────────┴─────────┴─────────┴────────┴──────┴──────────┴─────────┴───────┴──────┴──────────┴───────────────────┴──────┘
user@host:~/formae/1.AwsImport$
ID列の値はformaeのコマンド実行IDでした。
成功したようです。
formae status agentも実行してみます。
user@host:~/formae/1.AwsImport$ formae status agent
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
Structure Commands Resources
┌─────────┬───┐ ┌─────────────┬───┐ ┌─────────────┬────┐
│ Stacks │ 0 │ │ Total │ 1 │ │ Total │ 0 │
├─────────┼───┤ ├─────────────┼───┤ ├─────────────┼────┤
│ Targets │ 1 │ │ Successes │ 1 │ │ Managed │ 0 │
└─────────┴───┘ ├─────────────┼───┤ ├─────────────┼────┤
│ Failures │ 0 │ │ Unmanaged │ 0 │
├─────────────┼───┤ ├─────────────┼────┤
│ In Progress │ 0 │ │ Unmanaged % │ 0% │
└─────────────┴───┘ └─────────────┴────┘
Resource Types
┌─────────────┬───┐
│ Total Types │ 0 │
└─────────────┴───┘
user@host:~/formae/1.AwsImport$
こちらも問題ないようです。
ガイドに記載のコマンドを実行します。
user@host:~/formae/1.AwsImport$
user@host:~/formae/1.AwsImport$ formae inventory targets --query='discoverable:true'
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
┌────────────┬───────────┬──────────────┬─────────────────────────────────┐
│ Label │ Namespace │ Discoverable │ Config │
├────────────┼───────────┼──────────────┼─────────────────────────────────┤
│ my-account │ AWS │ true │ Region=ap-northeast-1, Type=AWS │
└────────────┴───────────┴──────────────┴─────────────────────────────────┘
Summary: Showing 1 of 1 total targets
user@host:~/formae/1.AwsImport$
AWSアカウントが認識されたようです。
AWS上のリソースを取得する
管理されていないリソースの一覧を取得してみます。
user@host:~/formae/1.AwsImport$
user@host:~/formae/1.AwsImport$ formae inventory resources --query="managed:false"
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
┌──────────────────────────┬───────────┬────────────────────────────────┬──────────────────────────┐
│ NativeID │ Stack │ Type │ Label │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ dopt-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::DHCPOptions │ dopt-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ igw-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::InternetGateway │ igw-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ acl-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::NetworkAcl │ acl-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ rtb-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::RouteTable │ rtb-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ sg-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::SecurityGroup │ sg-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ sgr-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::SecurityGroupEgress │ sgr-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ sgr-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::SecurityGroupIngress │ sgr-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ subnet-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::Subnet │ subnet-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ subnet-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::Subnet │ subnet-XXXXXXXXXXXXXXX │
├──────────────────────────┼───────────┼────────────────────────────────┼──────────────────────────┤
│ subnet-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::Subnet │ subnet-XXXXXXXXXXXXXXX │
└──────────────────────────┴───────────┴────────────────────────────────┴──────────────────────────┘
Summary: Showing 10 of 26 total resources (use --max-results 26 to see all)
user@host:~/formae/1.AwsImport$
表示されました。
デフォルトでは10件しか表示されないようです。
--max-resultsオプションを指定してみます。
user@host:~/formae/1.AwsImport$ formae inventory resources --query="managed:false" --max-results=100
oooooo ooooo oooo oooo ooooo ooo oooo
0oo o0o0 oo0o o0o 0oo ooo0o 0o0 0o 00 0o 00o
ooo 0oo o0 0o0 ooo ooo oo0 o0 0oo oo
ooo oo0 oo0 oo oo oo oo o00 o0oo
oooooo0 ooo oo oo oo oo oo o00 ooo
ooo oo 0oo oo oo oo oo 00 o00 o0o
ooo 0oo o0o oo oo oo oo o0 oooo0 ooo
ooo o00000oo oo oo o0 oo 0000o 0000o v0.85.2
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────────┬──────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ NativeID │ Stack │ Type │ Label │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────┼──────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ dopt-XXXXXXXXXXXXXXX │ unmanaged │ AWS::EC2::DHCPOptions │ dopt-XXXXXXXXXXXXXXX │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────┼──────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
<中略>
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────┼──────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ AWSServiceRoleForResourceExplorer │ unmanaged │ AWS::IAM::Role │ AWSServiceRoleForResourceExplorer │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────┼──────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ AWSServiceRoleForSupport │ unmanaged │ AWS::IAM::Role │ AWSServiceRoleForSupport │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────┼──────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ AWSServiceRoleForTrustedAdvisor │ unmanaged │ AWS::IAM::Role │ AWSServiceRoleForTrustedAdvisor │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────┴──────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Summary: Showing 26 of 26 total resources
user@host:~/formae/1.AwsImport$
すべて表示されました。
GUI上で確認した範囲では、formae が表示したリソースは実際に存在していました。
ただし、今回確認したのは ap-northeast-1 の小規模な検証環境のみです。
そのため、すべてのAWSサービスや複数リージョンで同じように検出できるかは、追加検証が必要です。
さいごに
formaeは、実際に存在するリソースを正として扱う思想のツールです。
そのため、コードで定義した状態を厳格に維持したい環境や、変更管理・承認フローを重視する本番環境では、現時点ではTerraformのほうが適している場面も多いと感じました。
一方で、個人開発や検証環境のように、そこまで厳格な変更管理を求められない場面では、formaeの柔軟さが活きる可能性があります。
また、検証中は公式ガイドどおりに進まない箇所もあり、まだ情報の正確性や安定性の面では不安が残ります。
この点からも、本番環境への導入は慎重に判断する必要がありそうです。
一方で、リソーススキャン自体は比較的少ない手順で実行でき、確認した範囲ではスキャン漏れも見当たりませんでした。
既存環境を把握し、コード化していくためのアプローチとしては、十分に可能性を感じるツールです。
まだ発展途上ではありますが、Terraformとは異なる思想を持つIaCツールとして非常に興味深い存在だと感じました。
今後も継続して検証し、Terraformとの使い分けや、実運用で活用できる場面を探っていきたいと思います。