もうこれにする。Pulumi AI で Cloudflare + Route 53 を楽チン IaC。
はじめに
仕事上、Cloudflare を CNAME セットアップでデモをする環境を用意しており、権威 DNS には Route 53 を使っています。
こいつ、新しいホストを Cloudflare に作るたびに Route 53 でも作業が発生して、地味にタルいなと感じつつ、タマにだからいいか、とそのままにしておりました。
(ワイルドカードもいやだし、Teraform も何か面倒くさいしなぁ…)
そんななか、Devdoc に Pulumi を見つけました。
恥ずかしながら、Devdoc を見るまで Pulumi って知りませんでしたが、試したところ、そのタルさをサクッと解消してくれたので、メモしておきます。
Pulumi AI
Devdoc のチュートリアルに沿ってもいいのですが、試している中で Pulumi AI に気づきまして、これを利用するのが普段インフラを管理しない私レベルの作業効率化にはウッテツケでした。
Pulumi にアカウント登録して Pulumi AI に行くと、やりたいことを入れるだけで、いろんな言語でサンプルを表示してくれる のです。今どき普通なの?すごい。
さっそく、例の面倒な作業(Route 53 に CNAME レコード作って Workers の Custom Domains で公開)を頼んでみます。
Create or modify an AWS route53 CNAME resource record and create a cloudflare workers script with a workers custom domain
と入れて、 TypeScript と Python の二種をご注文です。ポチッ。流れるようにコードや注意点を示してくれます。
その後気づいたんですが、日本語での注文も余裕で対応しておりました。素晴らしい。。
ハナから英語だろうという思い込みが足を引っ張ってました、、、反省です。
日本語でも英語でも、多少内容を変えるだけで、サンプルスクリプトも変わってくるのが面白いですね。いい感じのサンプルが来るまで、いくつか入れてみるのがいいかもです。
ということで、Pulumi AI が教えてくれたサンプルと、Devdoc および Pulumi の Docs があればなんとかなりそうです。
実際にやってみます。
GUI を使わなくても、CLI のなかで AI に注文できる ところも気に入りました。
ありたい姿にする
Pulumi でプロジェクトを作成するには、テンプレートを利用するのが定番のようで、Devdoc の Hello World チュートリアルも外部 URL をソースに呼んでます。
ただ、今回は AI を選びます。
~/Pulumi/cname-setup/r53 $ pulumi new -h
:
To create a project using Pulumi AI, either select `ai` from the first selection, or provide any of the following:
* `pulumi new --ai "<prompt>"`
* `pulumi new --language <language>`
* `pulumi new --ai "<prompt>" --language <language>`
:
Route 53
- プロジェクト作成
pulumi new
から ai
オプションを選びます。
TypeScript
で modify DNS resource records of existing AWS Route53 zones
を注文します。
Project Name や Stack Name(デフォルトは dev
)は指定のままで行きました。
~/Pulumi/cname-setup/r53 $ pulumi new
Would you like to create a project from a template or using a Pulumi AI prompt? ai
Please select a language for your project: TypeScript
Please input your prompt here ("a static website on AWS behind a CDN"):
modify DNS resource records of existing AWS Route53 zones
Sending prompt to Pulumi AI...
:
:
後追いですが、こちらも日本語で注文 OK でした。
-
pulumi config
作成
AWS のユーザープロファイルを Config に set しました。
ただ、Route 53 の権限だけ与えた AWS ユーザーを利用しているためか、後述 4 のデプロイ時に AWS から認証まわりのエラー retrieving caller identity from STS
、retrieving account information via iam:ListRoles
が出ました。
回避のために 2 つの skip を true で Config Options に追加しました。
~/Pulumi/cname-setup/r53 $ pulumi config set aws:profile r53admin -s dev
~/Pulumi/cname-setup/r53 $ pulumi config set aws:skipCredentialsValidation true -s dev
~/Pulumi/cname-setup/r53 $ pulumi config set aws:skipRequestingAccountId true -s dev
~/Pulumi/cname-setup/r53 $ cat Pulumi.dev.yaml
config:
aws:profile: r53admin
aws:skipCredentialsValidation: "true"
aws:skipRequestingAccountId: "true"
~/Pulumi/cname-setup/r53 $
-
index.ts
の編集
AI に作成してもらった index.ts
を Pulumi Doc など見ながら調整します。
import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
// Modify DNS resource records of an existing AWS Route53 Zone
const zoneName = 'oyama.cf.';
const zoneId = pulumi.output(aws.route53.getZone({ name: zoneName }, { async: true }))
.apply(zone => zone.zoneId);
const prefix = 'pulumi.';
const suffix = 'cdn.cloudflare.net.';
const record = new aws.route53.Record(prefix, {
zoneId: zoneId,
name: prefix,
type: "CNAME",
ttl: 300,
records: [ prefix + zoneName + suffix ],
allowOverwrite: true,
});
- デプロイ
pulumi up
...
途中で details
を選ぶとデプロイ内容を事前に確認できました。
no
で取りやめ、yes
でゴーです。
yes
のあとにエラーが出ることもあります。
~/Pulumi/cname-setup/r53 $ pulumi up
Previewing update (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Plan
+ pulumi:pulumi:Stack r53-dev create
+ └─ aws:route53:Record pulumi. create
Resources:
+ 2 to create
Do you want to perform this update? details
+ pulumi:pulumi:Stack: (create)
:
allowOverwrite: true
name : "pulumi."
records : [
[0]: "pulumi.oyama.cf.cdn.cloudflare.net."
]
ttl : 300
type : "CNAME"
zoneId : *
Do you want to perform this update? yes
Updating (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Status
+ pulumi:pulumi:Stack r53-dev created (1s)
+ └─ aws:route53:Record pulumi. created (46s)
Resources:
+ 2 created
Duration: 53s
- 動作確認
CNAME 拾えています。
~ $ dig pulumi.oyama.cf CNAME +norec +short @ns-418.awsdns-52.com
pulumi.oyama.cf.cdn.cloudflare.net.
Cloudflare
プロジェクトは Route 53 と別にしてます。
~/Pulumi/cname-setup/r53 $ cd ../cf
~/Pulumi/cname-setup/cf $
- プロジェクト作成
こちらも AI にサンプル作っていただきます。
a cloudflare worker with custom domains
~/Pulumi/cname-setup/cf $ pulumi new
Would you like to create a project from a template or using a Pulumi AI prompt? ai
Please select a language for your project: TypeScript
Please input your prompt here ("a static website on AWS behind a CDN"):
a cloudflare worker with custom domains
Sending prompt to Pulumi AI...
:
-
pulumi config
作成
スクリプト内で利用する Cloudfalre の API Token と Account ID を Config に入れました。
~/Pulumi/cname-setup/cf $ pulumi config set accountId --secret
~/Pulumi/cname-setup/cf $ pulumi config set cloudflare:apiToken --secret
value:
~/Pulumi/cname-setup/cf $ cat Pulumi.dev.yaml
config:
cf:accountId:
secure: AAAB..
cloudflare:apiToken:
secure: AAAB..
-
index.ts
の編集
AI に作成してもらった index.ts
を Pulumi Doc、Devdoc など見ながら調整します。
import * as cloudflare from "@pulumi/cloudflare";
import * as pulumi from "@pulumi/pulumi";
const config = new pulumi.Config();
const accountId = config.require("accountId");
const zoneName = "oyama.cf";
const prefix = "pulumi."
const zone = pulumi.output(cloudflare.getZone({
accountId: accountId,
name: zoneName,
}));
const zoneId = zone.id;
// Define a Worker script
const workerScript = new cloudflare.WorkerScript("my-worker", {
name: "pulumi-1st",
accountId: accountId, // Replace with your actual Cloudflare account ID
content: `
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
});
async function handleRequest(request) {
return new Response("Hello World", { status: 200 })
}
`,
});
// Assign the Worker script to a particular domain
const workerDomain = new cloudflare.WorkerDomain("my-worker-domain", {
accountId: accountId,
hostname: prefix + zoneName,
service: workerScript.name,
zoneId: zoneId,
});
// Export the live URL of the worker
export const workerUrl = pulumi.interpolate`https://${workerDomain.hostname}/`;
- デプロイ
pulumi up
...
~/Pulumi/cname-setup/cf $ pulumi up
Previewing update (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Plan
+ pulumi:pulumi:Stack cf-dev create
+ ├─ cloudflare:index:WorkerScript my-worker create
+ └─ cloudflare:index:WorkerDomain my-worker-domain create
Outputs:
workerUrl: "https://pulumi.oyama.cf/"
Resources:
+ 3 to create
Do you want to perform this update? details
+ pulumi:pulumi:Stack: (create)
:
+ cloudflare:index/workerScript:WorkerScript: (create)
:
accountId : "[secret]"
content : "\n addEventListener(\"fetch\", event => {\n event.respondWith(handleRequest(event.request))\n });\n async function handleRequest(request) {\n return new Response(\"Hello World\", { status: 200 })\n }\n "
name : "pulumi-1st"
+ cloudflare:index/workerDomain:WorkerDomain: (create)
:
accountId : "[secret]"
environment: "production"
hostname : "pulumi.oyama.cf"
service : "pulumi-1st"
zoneId : *
--outputs:--
workerUrl: "https://pulumi.oyama.cf/"
Do you want to perform this update? yes
Updating (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Status
+ pulumi:pulumi:Stack cf-dev created (6s)
+ ├─ cloudflare:index:WorkerScript my-worker created (1s)
+ └─ cloudflare:index:WorkerDomain my-worker-domain created (1s)
Outputs:
workerUrl: "https://pulumi.oyama.cf/"
Resources:
+ 3 created
Duration: 10s
- 動作確認
Outputs で表示した URL にアクセスしてみます。
無事 Worker が動いているようです。
~ $ curl https://pulumi.oyama.cf/
Hello World%
~ $
Devdoc のチュートリアルに習うとこんな感じでしょうか。pulumi stack output
から引いてます。
~/Pulumi/cname-setup/cf $ curl `pulumi stack output workerUrl`
Hello World%
~/Pulumi/cname-setup/cf $
なかったことにする
デモなどの場合、終わったのに環境を消すのを忘れてしまい、もう使わない Cloudflare のゴミ設定が私のアカウントにずっと残りっぱなしになってます。。。
もとに戻すのも pulumi destroy
... で済むのがいいですね。
## Cloudflare
~/Pulumi/cname-setup/cf $ pulumi destroy
Previewing destroy (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Plan
- pulumi:pulumi:Stack cf-dev delete
- ├─ cloudflare:index:WorkerDomain my-worker-domain delete
- └─ cloudflare:index:WorkerScript my-worker delete
Outputs:
- workerUrl: "https://pulumi.oyama.cf/"
Resources:
- 3 to delete
Do you want to perform this destroy? yes
Destroying (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Status
- pulumi:pulumi:Stack cf-dev deleted (0.38s)
- ├─ cloudflare:index:WorkerDomain my-worker-domain deleted (1s)
- └─ cloudflare:index:WorkerScript my-worker deleted (0.70s)
Outputs:
- workerUrl: "https://pulumi.oyama.cf/"
Resources:
- 3 deleted
Duration: 5s
The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained.
If you want to remove the stack completely, run `pulumi stack rm dev`.
## Route 53
~/Pulumi/cname-setup/cf $ cd ../r53
~/Pulumi/cname-setup/r53 $
~/Pulumi/cname-setup/r53 $ pulumi destroy
Previewing destroy (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Plan
- pulumi:pulumi:Stack r53-dev delete
- └─ aws:route53:Record pulumi. delete
Resources:
- 2 to delete
Do you want to perform this destroy? yes
Destroying (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/*
Type Name Status
- pulumi:pulumi:Stack r53-dev deleted (0.38s)
- └─ aws:route53:Record pulumi. deleted (57s)
Resources:
- 2 deleted
Duration: 1m1s
The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained.
If you want to remove the stack completely, run `pulumi stack rm dev`.
まとめ
必要に駆られたときの AI の力を見せていただきました。
タルさがちょっとだったので見て見ぬふりしてましたけど、AI が解決に乗り出す負荷を軽くしてくれました。これからの作業が楽になり、ミライ時間を手に入れました。
軽く触っただけで、やりたいことができたので十分ですが、さらに奥は深そうですね。
色々なサービス向けのプロバイダーがあるところを見ると、実際の現場では、浅いところから深いところまで、多く活用されているのではないかと想像します。
私の環境でも S3 や R2、GCP や OCI などデモに使ってたりするので、そっちも楽ができそうです。
ありがとう Pulumi AI これからもよろしく。
Discussion