このスクリプトの目的
AWSをある程度活用していくと、組織(Organizations)を駆使して複数のAWSアカウントを使い分けることが多いと思います。そのような場合に、横断的に情報を取得するサンプルコードを書いてみました。
データ全取得はそれなりに時間がかかるので、JSONファイルにデータを書き出してから活用するのがおすすめです。AWS SDKの型情報をそのまま使っておけば、何を利用するかはあとで決めることができます。
簡単な解説
まず組織内のアカウント一覧を取得し、取得した各アカウントにAssumeRoleしながら情報を収集していきます。
今回はec2インスタンスの取得だけを行っていますが、RDS等も簡単に追加できます。
Info[]型としてデータをまとめてJSONで書き出しているので、あとはこのJSONを読み込んで煮るなり焼くなりしてください。
サンプルコード
import {Account, Accounts} from "aws-sdk/clients/organizations";
import {EC2, Organizations, STS} from "aws-sdk";
import * as fs from "fs";
import {Instance} from "aws-sdk/clients/ec2";
import {CredentialsOptions} from "aws-sdk/lib/credentials";
// 情報を取得するために利用するRole
// プログラムから、各アカウントのこのRoleにAssumeRoleできる必要がある
const AccessRole = "OrganizationAccountAccessRole"
type Info = {
account: Account
instances: Instance[]
}
async function assumeRole(account: Account): Promise<CredentialsOptions> {
const sts = new STS()
const RoleArn = `arn:aws:iam::${account.Id}:role/${AccessRole}`
console.log(`Switch to ${RoleArn}`)
const {Credentials} = await sts.assumeRole({
RoleArn,
RoleSessionName: "FetchInstances"
}).promise()
if (!Credentials) {
throw new Error("Failed to switch Role")
}
// CredentialsOptionsと大文字小文字が違うので、ここで変換
return {
accessKeyId: Credentials.AccessKeyId,
secretAccessKey: Credentials.SecretAccessKey,
sessionToken: Credentials.SessionToken,
}
}
async function getAccounts(org?: Organizations, NextToken?: string): Promise<Accounts> {
console.log(`getAccount ${NextToken || "initial"}`)
// ap-northeast-1にはOrganizationsエンドポイントが存在しない。
if (!org) {
org = new Organizations({
region: "us-east-1",
endpoint: "https://organizations.us-east-1.amazonaws.com",
})
}
let result: Account[] = []
const accounts = await org.listAccounts({
NextToken,
}).promise()
if (accounts.Accounts) {
result = result.concat(accounts.Accounts)
}
if (accounts.NextToken) {
result = result.concat(await getAccounts(org, accounts.NextToken))
}
return result
}
async function getInstances(ec2: EC2, NextToken?: string): Promise<Instance[]> {
console.log(`getInstances ${NextToken || "initial"}`)
const instances = await ec2.describeInstances({
NextToken
}).promise()
if (!instances.Reservations) {
return []
}
let result: Instance[] = []
for (const r of instances.Reservations) {
if (r.Instances) {
result = result.concat(r.Instances)
}
}
if (instances.NextToken) {
result = result.concat(await getInstances(ec2, instances.NextToken))
}
return result
}
(async function run() {
// 組織内のアカウントを全部取得
const accounts = await getAccounts()
const result: Info[] = []
for (const account of accounts) {
// 個別のアカウントにAssumeRoleしながら情報を取得する
console.log(`Processing account ${account.Id} (${account.Name})`)
const credentials = await assumeRole(account)
result.push({
account,
instances: await getInstances(new EC2({credentials})),
})
}
const filepath = "./result.json"
console.log(`Write result to ${filepath}`)
await new Promise(r => fs.writeFile(filepath, JSON.stringify(result, null, 2), r))
})()