AWS CDKのConstructを自作してgitlabにpushし、別リポジトリから利用してみる ～AWS100本ノック～ 22/100

## はじめに

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/a71ddb45-7341-8ecf-0d12-6ab61e7ddcc1.png)

みなさん、CDKでインフラ構築する際に共通化などしていますか？
同じようなAWS構成を別プロジェクトで利用することってよくありますよね？

今回は、`自作Constructを特定のリポジトリにpushし、別リポジトリからnpm installで利用してデプロイする`を試してみます。
※今回は、別リポジトリからnpm installできることをゴールとするため、細かい説明は省略します

## 動作環境・使用するツールや言語など
- CDK(TypeScript)
- Gitlab
- npm

## カスタムConstructを自作して、Gitlab Packeage Registryにpushしてみる

それでは実際に作りながら、説明していきます。

### 新規CDKプロジェクト(Construct)の作成

まず以下コマンドで、CDK(TypeScript)のConstructライブラリのひな形を作成します。
```bash
cdk init lib --language=typescript

👇以下のファイルが作られます
.git/
.gitignore
.npmignore
jest.config.js
lib/
node_modules/
package.json
package-lock.json
README.md
test/
tsconfig.json
```

lib/index.tsを開くと以下のようなファイルが出来ていると思います。
```ts:lib/index.ts
// import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
// import * as sqs from 'aws-cdk-lib/aws-sqs';

export interface CustomBucketProps {
  // Define construct properties here
}

export class CustomBucket extends Construct {

  constructor(scope: Construct, id: string, props: CustomBucketProps = {}) {
    super(scope, id);

    // Define construct contents here

    // example resource
    // const queue = new sqs.Queue(this, 'CdkTest2Queue', {
    //   visibilityTimeout: cdk.Duration.seconds(300)
    // });
  }
}
```


### カスタムConstructの作成
CustomBucketを以下のように修正して、S3バケットを作るコンストラクタを作ります。

```ts:lib/index.ts
import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';

export interface CustomBucketProps {
  readonly bucketNamePrefix: string;
}

export class CustomBucket extends Construct {

  constructor(scope: Construct, id: string, props: CustomBucketProps) {
    super(scope, id);

    // Get AWS Account ID
    const { accountId } = new cdk.ScopedAws(this);

    // Create Bucket
    new s3.Bucket(this, `${props.bucketNamePrefix}-Bucket`, {
      bucketName: `${props.bucketNamePrefix}-${accountId}`,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      publicReadAccess: false,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE,
      enforceSSL: true,
      versioned: true,
    });
  }
}
```

ビルドして問題ないことを確認します。
```bash
npm run build

> custom-bucket@0.1.0 build
> tsc
```

### Gitlab Packeage Registryの準備
Gitlab Packeage Registryを利用することで、npmなどのパッケージをgitlab上で管理することが出来ます。

リポジトリを作成し、`Settings`の`Repository`をクリックします。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/dae4d189-78f8-7a2f-bd26-5fd0bedf2276.png)

`Deploy tokens`を開き、
- `Name`:任意のトークン名
- `Username`:任意のユーザ名
- `Scopes`:`read_package_registry`と`write_package_registry`
を入力し、`Create deploy token`をクリックすると、トークンが発行されます。

:::note warn
作成した時にしかトークンは確認できないので、必ずどこかに保存するようにしましょう。
:::

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/0948335b-b69c-0704-3098-87d05b24d01e.png)


### Gitlab Packeage Registryにpush

準備が出来たので実際にpushしていきます。
`Pacakges and registries`の`Package Registry`をクリックします。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/063db04a-0ee5-0249-efe6-b90697c1a4ad.png)

またpushしていないので、一覧には何も表示されないはずです。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/32e2edb0-7f5c-b659-35f5-74bc3ce86e2d.png)


package.jsonを以下のように修正します。
- `name`:npm isntallする際のパッケージ名になります
- `version`:どのバージョンでpushするか指定します

```json:package.json
{
  "name": "@nakazato-cdk-assets/my-construct",
  "version": "0.1.0",
  "description": "this package is original cdk construct",
  "main": "lib/index.js",
  "scripts": {
    "build": "tsc",
    "prepublishOnly": "npm run build"
  },
  "dependencies": {
    "aws-cdk-lib": "^2.0.0",
    "constructs": "^10.0.0"
  },
  "author": "m.nakazato",
  "license": "ISC"
}

```

`.npmrc`ファイルを作成し、以下のように記述します。
```npmrc:.npmrc
@nakazato-cdk-assets:registry=https://${ドメイン名}/api/v4/projects/${プロジェクトID}/packages/npm/
//${ドメイン名}/api/v4/projects/1834/packages/npm/:_authToken="${NPM_TOKEN}"
```

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/33b4b791-fa81-b440-af94-b10a626efd7c.png)


```
NPM_TOKEN=${先ほど作成したデプロイトークン} npm publish
```

Registry一覧を見ると、0.1.0で無事pushされました:smile:
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87924/77c1a673-bed9-57b2-32c6-a1fdd98a820f.png)



## Constructをnpm installして、デプロイしてみる
では、最後に先ほどpushしたConstructをインストールして、デプロイしてみましょう！

### 新規CDKプロジェクト(App)の作成
まず、以下コマンドでCDK(TypeScript)のプロジェクトを作成します。
```bash
cdk init lib --language=typescript
```

### パッケージをnpm install
`package.json`に先ほどpushしたパッケージを指定します。

```diff_json:package.json
{
  "name": "cdk-test",
  "version": "0.1.0",
  "bin": {
    "cdk-test": "bin/cdk-test.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@types/jest": "^29.5.5",
    "@types/node": "20.6.3",
    "aws-cdk": "2.134.0",
    "jest": "^29.7.0",
    "ts-jest": "^29.1.1",
    "ts-node": "^10.9.1",
    "typescript": "~5.2.2"
  },
  "dependencies": {
+   "@nakazato-cdk-assets/construct-lib": "^0.1.0",
    "aws-cdk-lib": "2.134.0",
    "constructs": "^10.0.0",
    "source-map-support": "^0.5.21"
  }
}
```

.npmrcファイルを先ほどと同様に作成します。
```npmrc:.npmrc
@nakazato-cdk-assets:registry=https://${ドメイン名}/api/v4/projects/${プロジェクトID}/packages/npm/
//${ドメイン名}/api/v4/projects/1834/packages/npm/:_authToken="${NPM_TOKEN}"
```

npm installを実行します。
```
NPM_TOKEN=${先ほど作成したデプロイトークン} npm install @nakazato-cdk-assets/construct-lib
```

スタックファイルを以下のように修正します。
※CdkTestStackというスタックにした場合
```ts:CdkTestStack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as customConstruct from '@nakazato-cdk-assets/construct-lib';

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

    new customConstruct.CustomBucket(this, 'CustomConstruct', {
        bucketNamePrefix: 'nakazato-test',
    });
  }
}

```

### S3バケットの作成
デプロイを実行して、S3バケットを作ります。

```bash
npx cdk deploy --all

✨  Synthesis time: 10.22s

This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────────────────────────────────────────┬────────┬─────────────────────────────────────────────────┬─────────────────────────────────────────────────┬────────────────────────────────────────────────────┐
│   │ Resource                                        │ Effect │ Action                                          │ Principal                                       │ Condition                                          │ 
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤ 
│ + │ ${CustomConstruct/nakazato-test-Bucket.Arn}     │ Deny   │ s3:*                                            │ AWS:*                                           │ "Bool": {                                          │ 
│   │ ${CustomConstruct/nakazato-test-Bucket.Arn}/*   │        │                                                 │                                                 │   "aws:SecureTransport": "false"                   │ 
│   │                                                 │        │                                                 │                                                 │ }                                                  │ 
│ + │ ${CustomConstruct/nakazato-test-Bucket.Arn}     │ Allow  │ s3:DeleteObject*                                │ AWS:${Custom::S3AutoDeleteObjectsCustomResource │                                                    │ 
│   │ ${CustomConstruct/nakazato-test-Bucket.Arn}/*   │        │ s3:GetBucket*                                   │ Provider/Role.Arn}                              │                                                    │ 
│   │                                                 │        │ s3:List*                                        │                                                 │                                                    │ 
│   │                                                 │        │ s3:PutBucketPolicy                              │                                                 │                                                    │ 
├───┼─────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────┼─────────────────────────────────────────────────┼────────────────────────────────────────────────────┤ 
│ + │ ${Custom::S3AutoDeleteObjectsCustomResourceProv │ Allow  │ sts:AssumeRole                                  │ Service:lambda.amazonaws.com                    │                                                    │ 
│   │ ider/Role.Arn}                                  │        │                                                 │                                                 │                                                    │ 
└───┴─────────────────────────────────────────────────┴────────┴─────────────────────────────────────────────────┴─────────────────────────────────────────────────┴────────────────────────────────────────────────────┘ 
IAM Policy Changes
┌───┬───────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource                                                  │ Managed Policy ARN                                                                           │
├───┼───────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Custom::S3AutoDeleteObjectsCustomResourceProvider/Role} │ {"Fn::Sub":"arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"} │
└───┴───────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
CdkTestStack: deploying... [1/1]
CdkTestStack: creating CloudFormation changeset...
CdkTestStack | 0/7 | 18:01:42 | REVIEW_IN_PROGRESS   | AWS::CloudFormation::Stack  | CdkTestStack User Initiated
CdkTestStack | 0/7 | 18:01:48 | CREATE_IN_PROGRESS   | AWS::CloudFormation::Stack  | CdkTestStack User Initiated
CdkTestStack | 0/7 | 18:01:50 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata          | CDKMetadata/Default (CDKMetadata)
CdkTestStack | 0/7 | 18:01:50 | CREATE_IN_PROGRESS   | AWS::S3::Bucket             | CustomConstruct/nakazato-test-Bucket (CustomConstructnakazatotestBucket1EAD1A48)
CdkTestStack | 0/7 | 18:01:51 | CREATE_IN_PROGRESS   | AWS::IAM::Role              | Custom::S3AutoDeleteObjectsCustomResourceProvider/Role (CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092)
CdkTestStack | 0/7 | 18:01:52 | CREATE_IN_PROGRESS   | AWS::S3::Bucket             | CustomConstruct/nakazato-test-Bucket (CustomConstructnakazatotestBucket1EAD1A48) Resource creation Initiated
CdkTestStack | 0/7 | 18:01:52 | CREATE_IN_PROGRESS   | AWS::IAM::Role              | Custom::S3AutoDeleteObjectsCustomResourceProvider/Role (CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092) Resource creation Initiated
CdkTestStack | 0/7 | 18:01:52 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata          | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
CdkTestStack | 1/7 | 18:01:52 | CREATE_COMPLETE      | AWS::CDK::Metadata          | CDKMetadata/Default (CDKMetadata)
CdkTestStack | 2/7 | 18:02:07 | CREATE_COMPLETE      | AWS::S3::Bucket             | CustomConstruct/nakazato-test-Bucket (CustomConstructnakazatotestBucket1EAD1A48) 
CdkTestStack | 3/7 | 18:02:09 | CREATE_COMPLETE      | AWS::IAM::Role              | Custom::S3AutoDeleteObjectsCustomResourceProvider/Role (CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092) 
CdkTestStack | 3/7 | 18:02:10 | CREATE_IN_PROGRESS   | AWS::Lambda::Function       | Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler (CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F)
CdkTestStack | 3/7 | 18:02:11 | CREATE_IN_PROGRESS   | AWS::S3::BucketPolicy       | CustomConstruct/nakazato-test-Bucket/Policy (CustomConstructnakazatotestBucketPolicy5CEEDA5F)
CdkTestStack | 3/7 | 18:02:12 | CREATE_IN_PROGRESS   | AWS::Lambda::Function       | Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler (CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F) Resource creation Initiated
CdkTestStack | 3/7 | 18:02:13 | CREATE_IN_PROGRESS   | AWS::S3::BucketPolicy       | CustomConstruct/nakazato-test-Bucket/Policy (CustomConstructnakazatotestBucketPolicy5CEEDA5F) Resource creation Initiated
CdkTestStack | 3/7 | 18:02:13 | CREATE_IN_PROGRESS   | AWS::Lambda::Function       | Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler (CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F) Eventual consistency check initiated
CdkTestStack | 4/7 | 18:02:13 | CREATE_COMPLETE      | AWS::S3::BucketPolicy       | CustomConstruct/nakazato-test-Bucket/Policy (CustomConstructnakazatotestBucketPolicy5CEEDA5F)
CdkTestStack | 5/7 | 18:02:19 | CREATE_COMPLETE      | AWS::Lambda::Function       | Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler (CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F) 
CdkTestStack | 5/7 | 18:02:19 | CREATE_IN_PROGRESS   | Custom::S3AutoDeleteObjects | CustomConstruct/nakazato-test-Bucket/AutoDeleteObjectsCustomResource/Default (CustomConstructnakazatotestBucketAutoDeleteObjectsCustomResourceE418102A)
CdkTestStack | 5/7 | 18:02:21 | CREATE_IN_PROGRESS   | Custom::S3AutoDeleteObjects | CustomConstruct/nakazato-test-Bucket/AutoDeleteObjectsCustomResource/Default (CustomConstructnakazatotestBucketAutoDeleteObjectsCustomResourceE418102A) Resource creation Initiated
CdkTestStack | 6/7 | 18:02:21 | CREATE_COMPLETE      | Custom::S3AutoDeleteObjects | CustomConstruct/nakazato-test-Bucket/AutoDeleteObjectsCustomResource/Default (CustomConstructnakazatotestBucketAutoDeleteObjectsCustomResourceE418102A)
CdkTestStack | 7/7 | 18:02:22 | CREATE_COMPLETE      | AWS::CloudFormation::Stack  | CdkTestStack

 ✅  CdkTestStack

✨  Deployment time: 42.65s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:***:stack/CdkTestStack/810a1610-d3e8-11ef-a582-06318d941fa5

✨  Total time: 52.87s
```




## おわりに
今回は、AWS CDKのConstructを自作してgitlabにpushし、別リポジトリから利用してみることを試してみました！
どういった単位でConstructを作るか、などは人それぞれ考えがあるとは思いますが、チームで統一できていれば開発効率がかなり上がっていきそうですね :smile: