[小ネタ]AWS CDKで外部のjsonファイル内文字列の変換方法 in TypeScript

![001samune.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3984127/dc9b4b45-e037-1f6a-f739-8d839f221665.png)

結論：**JSON**オブジェクトの静的メソッド(**JSON.parse()**)とString オブジェクトのインスタンスメソッド(**replace()**)を使用します。

# 概要
IaCでコード化する際、IAMポリシーをjsonファイルとして外だしする記述方法があると思います。
そういった記述方式の際、流用性を高めるにはアカウントIDやリージョン名などはハードコーディングしないのが望ましいです。
Terraformだと`templatefile`関数を使って動的に対応することが可能です。同じようなことをCDK(TypeScript)でもやりたいと思って調べたところ、私の調査力ではCDK APIに見つけられず、たどり着いたのがJavaScriptの標準オブジェクトとメソッドの組み合わせでした。
本記事ではその手法を備忘録として残します。

https://developer.hashicorp.com/terraform/language/functions/templatefile


# 実践
簡単ですが、IAMポリシーを作成してみます。

https://github.com/tyskJ/aws-cdk-replace-variables-in-json-files

### IAMポリシー用jsonファイル
作成するIAMポリシー用jsonファイルを以下のように記述しました。
変換したい個所を`{任意の文字列}`で記述しています。
```test-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "TestPolicy",
      "Effect": "Allow",
      "Action": ["ec2:StartInstances"],
      "Resource": ["arn:{Partition}:ec2:{Region}:{Account}:instance/*"]
    }
  ]
}
```

### stackファイル
デプロイするstackファイルを以下のように記述しました。
```cdk-app-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as iam from "aws-cdk-lib/aws-iam";
import * as path from "path";
import * as fs from "fs";

export class CdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 擬似パラメータインスタンス作成
    const pseudo = new cdk.ScopedAws(this);

    // JSONファイルパス
    const filePath = path.join(`${__dirname}`, "./json/test-policy.json");

    // JSONファイルをJSON文字列として読み込み
    const jsonData = fs.readFileSync(filePath, "utf8");

    // 文字列置換&JSONパース(JavaScriptオブジェクトに変換)
    const jsonPolicy = JSON.parse(
      jsonData
        .replace(/{Partition}/g, pseudo.partition)
        .replace(/{Region}/g, pseudo.region)
        .replace(/{Account}/g, pseudo.accountId)
    );

    // IAMポリシー作成
    new iam.ManagedPolicy(this, "policy", {
      managedPolicyName: "iam-policy",
      document: iam.PolicyDocument.fromJson(jsonPolicy),
    });
  }
}
```

### 処理フロー
#### JSONファイルをJSON文字列として読み込み
初めに、指定のjsonファイルを`readFileSync()`でjson文字列として読み込みます。
```ts
    const jsonData = fs.readFileSync(filePath, "utf8");
```

#### 文字列置換し、JavaScriptオブジェクトに変換
`replace()`を使用して、`jsonData`内の文字列を変換します。`/g`をつけることで、一括置換が可能です。
```ts
    const jsonPolicy = JSON.parse(
      jsonData
        .replace(/{Partition}/g, pseudo.partition)
        .replace(/{Region}/g, pseudo.region)
        .replace(/{Account}/g, pseudo.accountId)
    );
```

### CFnテンプレート確認
`cdk synth --no-version-reporting --no-path-metadata`で出力されるCFnテンプレートがこちら。
AWS CloudFormationで使用できる擬似パラメータに変換されていることが確認できます。

```yaml
Resources:
  policyE16B4B70:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Description: ""
      ManagedPolicyName: iam-policy
      Path: /
      PolicyDocument:
        Statement:
          - Action: ec2:StartInstances
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:"
                  - Ref: AWS::Partition
                  - ":ec2:"
                  - Ref: AWS::Region
                  - ":"
                  - Ref: AWS::AccountId
                  - :instance/*
            Sid: TestPolicy
        Version: "2012-10-17"
```


### デプロイして確認
`cdk deploy`で作成されたIAMポリシーがこちら。問題なく擬似パラメータが変換されているのが確認できます。
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3984127/bdfb550c-4d32-4c5d-c9c8-46e7a427ff7b.png)



# まとめ
今回は、CDK(TypeScript)で外部のjsonファイル内の文字列を変換する方法を記事にしました。
もっとスマートなやり方があるかもしれませんが、もしご存じの方がいらっしゃいましたらアドバイスいただけると幸いです。


# 参考資料
### リファレンス
* [MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/JavaScript)