GitHubActions + TerraformでECRを作成してみた

# 背景・目的
以前、下記の記事でECSの知識の整理や、VPC等の環境を構築しました。
今後、ECSでバックエンドを構築するにあたり、コンテナイメージレジストリであるECRについて整理をしてみます。
また、Terraformで環境を構築してみます。

- [Amazon ECSについて整理してみた](https://qiita.com/zumax/items/e241894713016871301e)
- [GitHubActionsとTerraformでNWを作成してみた](https://qiita.com/zumax/items/5a9435d0d7371a8c5033)


# まとめ
下記に特徴をまとめます。

|特徴|説明|
|:--|:--|
|概要|・マネージドなコンテナイメージレジストリサービス<BR>・安全でスケーラブル<BR>・信頼性が高い<BR>・IAMを使用したリソースベースのアクセス許可を持つプライベートリポジトリをサポート<BR><BR>・下記の管理が可能<BR>　・Dockerイメージ<BR>　・Open Container Initiative（OCI）イメージ<BR>　・OCI互換アーティファクト|
|コンポーネント|・レジストリ<BR>・認証トークン<BR>・リポジトリ<BR>・リポジトリポリシー<BR>・イメージ|
|機能|・ライフサイクルポリシー<BR>・イメージスキャン<BR>・クロスリージョンおよびクロスアカウントレプリケーション<BR>・プルスルーキャッシュルール|
|料金|・ストレージ（0.10USD / GiB /月）<BR>・プライベートリポジトリから転送されたデータ<BR>　・受信:0.00USD / GB<BR>　・送信:0.114USD/GB 〜 0.084 USD / GB<BR>・パブリックリポジトリから転送されたデータ<BR>　・受信:0.00USD / GB<BR>　・送信<BR>　　・AWSアカウントを使用しない場合：0.00USD / GB<BR>　　・AWSアカウントを使用しない場合：0.00 USD/GB※1<BR>・イメージスキャン<BR>　・最初にスキャンされた時： 0.11 USD / イメージ<BR>　・連続スキャン： 0.01 USD / 月<BR><BR>※1:AWS以外のリージョンへの5TB/月を超える場合、0.09 USD /GB、任意のAWSリージョンへの任意の量のデータ 0.00 USD /GB|
|Amazon ECR でのプライベートレジストリ認証|Docker CLI単体では、IAM認証をサポートしていないため追加の手順が必要<BR>・Amazon ECR 認証情報ヘルパーを使用する<BR>・認可トークンを使用する<BR>・HTTP API 認証を使用する|
|プライベートリポジトリ|名前空間をサポートしている|
|イメージをスキャンする|基本スキャンと拡張スキャンがある|
|プルスルーキャッシュルール|プルスルーキャッシュルールにより、アップストリームレジストリをECRプライベートレジストリと同期できる<BR><BR>サポートしているキャッシュルールは、下記の通り<BR>　・Docker Hub<BR>　・Microsoft Azure コンテナレジストリ<BR>　・GitHub コンテナレジストリ<BR>　・GitLab コンテナレジストリ (認証が必要）<BR>　・Amazon ECR パブリック<BR>　・Kubernetes コンテナイメージレジストリ<BR>　・Quay (認証は不要)|
|ライフサイクルポリシー|プライベートリポジトリ内のイメージのライフサイクル管理を詳細に制御できる|

# 概要
[Amazon Elastic Container Registry とは](https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/what-is-ecr.html)のドキュメントを基に整理します。

> Amazon Elastic Container Registry (Amazon ECR) は、安全でスケーラブル、信頼性の高い AWS マネージドコンテナイメージレジストリサービスです。Amazon ECR は、 AWS IAM を使用したリソースベースのアクセス許可を持つプライベートリポジトリをサポートします。これは、指定されたユーザーまたは Amazon EC2 インスタンスがコンテナリポジトリとイメージにアクセスできるようにするためです。任意の CLI を使用して、Docker イメージ、Open Container Initiative (OCI) イメージ、および OCI 互換アーティファクトをプッシュ、プル、管理することが可能です。

- マネージドなコンテナイメージレジストリサービス
- 安全でスケーラブル
- 信頼性が高い
- IAMを使用したリソースベースのアクセス許可を持つプライベートリポジトリをサポート
- 下記の管理が可能
    - Dockerイメージ
    - Open Container Initiative（OCI）イメージ
    - OCI 互換アーティファク

## Amazon ECR のコンポーネント
> Amazon ECR には、次のコンポーネントが含まれています。

> レジストリ
Amazon ECR プライベートレジストリが各 AWS アカウントに提供されるため、レジストリに 1 つ以上のリポジトリを作成し、Docker イメージ、Open Container Initiative (OCI) イメージ、および OCI 互換アーティファクトを保存できます。詳細については、「Amazon ECR プライベートレジストリ」を参照してください。

- レジストリは、各アカウントごとに用意される

> 認証トークン
クライアントがイメージをプッシュおよびプルするには、 AWS ユーザーとして Amazon ECR プライベートレジストリに対して認証する必要があります。詳細については、「Amazon ECR でのプライベートレジストリ認証」を参照してください。

- 認証トークンは、AWSユーザとしてプライベートレジストリに対して認証する

> リポジトリ
Amazon ECR リポジトリには、Docker イメージ、Open Container Initiative (OCI) イメージ、および OCI 互換アーティファクトが含まれます。詳しくは、「Amazon ECR プライベートリポジトリ」を参照してください。

- ECRリポジトリは下記の管理が可能
    - Dockerイメージ
    - Open Container Initiative（OCI）イメージ
    - OCI 互換アーティファク


> リポジトリポリシー
リポジトリポリシーを使用して、リポジトリとリポジトリ内のコンテンツへのアクセス権を制御できます。詳細については、「Amazon ECR のプライベートリポジトリポリシー」を参照してください。

- リポジトリポリシーを使用して、アクセス権の制御が可能

> イメージ
リポジトリには、コンテナイメージをプッシュおよびプルできます。開発システムでこれらのイメージは、ローカルに使用することや、Amazon ECS タスク定義と Amazon EKS ポッド仕様で使用することができます。詳細については、「Amazon ECS での Amazon ECR イメージの使用」および「Amazon EKS での Amazon ECR イメージの使用」を参照してください。

- コンテナイメージをプッシュ、プルが可能
- ローカル、ECSタスク定義、EKSポッドで使用が可能

## Amazon ECR の機能
> Amazon ECR には次の機能があります。

> - ライフサイクルポリシーを使用すると、リポジトリ内のイメージのライフサイクルを管理できます。未使用のイメージをクリーンアップするルールを定義します。ルールはリポジトリに適用する前にテストできます。詳しくは、「Amazon ECR のライフサイクルポリシーを使用してイメージのクリーンアップを自動化する」を参照してください。

- ライフサイクルポリシーにより、イメージのライフサイクルを管理できる

> - イメージスキャンは、コンテナイメージ内のソフトウェアの脆弱性を特定するのに役立ちます。各リポジトリはプッシュ時にスキャンするように設定できます。その場合、リポジトリにプッシュされる新しい各イメージが確実にスキャンされます。その後、イメージスキャンの結果を取得できます。詳しくは、「Amazon ECR でソフトウェアの脆弱性がないかイメージをスキャンする」を参照してください。

- イメージスキャンにより、コンテナイメージ内のソフトウェアの脆弱性の特定に役立つ

> - クロスリージョンおよびクロスアカウントレプリケーションを使用すると、必要な場所にイメージを簡単に作成できます。これは、レジストリ設定として、リージョンごとに構成されます。詳細については、「Amazon ECR のプライベートレジストリ設定」を参照してください。

- クロスリージョン、クスアカウントレプリケーションにより、必要な場所にコピーが可能

> - プルスルーキャッシュルールは、プライベート Amazon ECR レジストリのアップストリームレジストリ内のリポジトリのキャッシュ方法を提供します。プルスルーキャッシュルールを使用して、Amazon ECR は定期的にアップストリームレジストリに問い合わせて、Amazon ECR プライベートレジストリにキャッシュされたイメージが最新であることを確認します。詳細については、「アップストリームレジストリと Amazon ECR プライベートレジストリを同期する」を参照してください。


- プルスルーキャッシュルールにより、ECRレジストリ内のアップストリームレジストリ内のキャッシュが可能

## Amazon ECR の料金表
[Amazon Elastic Container Registry の料金](https://aws.amazon.com/jp/ecr/pricing/)を基に整理しています。

> ストレージ:
プライベートリポジトリまたはパブリックリポジトリに保存されたデータのストレージは、GB/月あたり 0.10USD です。

- 0.10USD / GiB /月

※ 下記は、東京リージョンの料金です。

> プライベートリポジトリから転送されたデータ:
データ受信	
すべてのデータ受信	0.00USD/GB
データ送信 **	
次の 9.999 TB/月	0.114USD/GB
次の 40 TB/月	0.089USD/GB
次の 100 TB/月	0.086USD/GB
150 TB/月超	0.084USD/GB

※ 下記は、東京リージョンの料金です。

> 公開リポジトリから転送されたデータ:
料金
データ受信	
すべてのデータ受信 (イン)	0.00 USD/GB
データ送信 ***	
AWS アカウントを使用しない場合	
500 GB/月	0.00 USD/GB
AWS アカウントを使用する場合	
5 TB/月	0.00 USD/GB
AWS 以外のリージョンへの 5 TB/月を超えるデータ	0.09 USD/GB
任意の AWS リージョンへの任意の量のデータ	0.00 USD/GB

### イメージスキャンの料金
[Amazon Inspector の料金表](https://aws.amazon.com/jp/inspector/pricing/)を基に整理します。

> Amazon ECR コンテナイメージスキャン: Amazon Inspector スキャン用に設定された Amazon ECR にプッシュされた各コンテナイメージは、ソフトウェアの脆弱性について評価されます。1 か月の総費用は、Amazon ECR にプッシュされたときに最初にスキャンされたイメージの数と、それらのイメージが 1 か月に再スキャンされた回数の組み合わせに基づいています。

- スキャンの料金は、プッシュされたときのイメージ数＋一ヶ月に再スキャンされた回数の組み合わせ

※下記は、東京リージョンの料金です。

> ECR コンテナイメージスキャン	
1 か月あたりの Amazon ECR へのプッシュ時に最初にスキャンされたコンテナイメージの数	イメージごとに 0.11USD
1 か月あたりの連続スキャン用に設定された Amazon ECR のコンテナイメージの自動再スキャンの数	リスキャンごとに 0.01USD


- 最初にスキャンされた時： 0.11 USD / イメージ
- 連続スキャン： 0.01 USD / 月


## Amazon ECR プライベートレジストリ
### プライベートレジストリの概念
> デフォルトのプライベートレジストリの URL は https://aws_account_id.dkr.ecr.us-west-2.amazonaws.com です。

- URLは、アカウントID.dkr.ecr.リージョン名.amazonaws.com

> デフォルトでは、アカウントにはプライベートリポジトリ内のリポジトリへの読み取りおよび書き込み許可があります。ただし、ユーザーには、Amazon ECR APIs を呼び出したり、プライベートリポジトリとの間でイメージをプッシュまたはプルしたりするためのアクセス許可が必要です。Amazon ECR には、さまざまなレベルでユーザーアクセスを制御するための管理ポリシーが複数用意されています。詳細については、「Amazon Elastic Container Registry のアイデンティティベースのポリシーの例」を参照してください。

- アカウントには、デフォルトでRead/Write権限がある
- ユーザには、改めて権限が必要

> プライベートレジストリに対して Docker クライアントを認証し、docker push コマンドと docker pull コマンドを使用してそのレジストリ内のリポジトリとの間でイメージをプッシュおよびプルできるようにする必要があります。詳細については、「Amazon ECR でのプライベートレジストリ認証」を参照してください。

- プライベートレジストリに対して、Dockerクライアントの認証が必要

> プライベートリポジトリは、 ユーザーアクセスポリシーとリポジトリポリシーによって制御できます。リポジトリポリシーの詳細については、「Amazon ECR のプライベートリポジトリポリシー」を参照してください。

- アクセスポリシー＋リポジトリポリシーの2つがある

> プライベートレジストリのリポジトリは、プライベートレジストリのレプリケーションを設定することで、プライベートレジストリ内および複数のアカウント間の複数のリージョンにわたってでレプリケートできます。詳細については、「Amazon ECR でのプライベートイメージのレプリケーション」を参照してください。

- レプリケートがある

#### Amazon ECR でのプライベートレジストリ認証
> AWS Management Console、、または AWS SDKs を使用して AWS CLI、プライベートリポジトリを作成および管理できます。また、これらの方法を使用して、イメージの一覧表示や削除などのいくつかのアクションをイメージで実行できます。これらのクライアントは標準の AWS 認証方法を使用します。Amazon ECR API を使用してイメージをプッシュおよびプルできますが、Docker CLI または言語固有の Docker ライブラリの使用をお勧めします。

- AWS認証方法を使用する

> Docker CLI は、ネイティブの IAM 認証方法をサポートしていません。Amazon ECR が、Docker のプッシュ要求とプル要求を認証および認可できるようにするには、追加の手順を実行する必要があります。

- Docker CLI単体では、IAM認証をサポートしていないため追加の手順が必要
    - Amazon ECR 認証情報ヘルパーを使用する
    - 認可トークンを使用する
    - HTTP API 認証を使用する

## Amazon ECR プライベートリポジトリ
> Amazon ECR プライベートリポジトリには、Docker イメージ、Open Container Initiative (OCI) イメージ、および OCI 互換アーティファクトが含まれています。イメージリポジトリを作成、モニタリング、削除し、Amazon ECR API オペレーションまたは Amazon ECR コンソールのリポジトリセクションを使用して、イメージリポジトリにアクセスできるユーザーを制御するアクセス許可を設定できます。Amazon ECR は Docker CLI とも統合されているため、開発環境からリポジトリにイメージをプッシュおよびプルできます。


- プライベートリポジトリには、下記のものがある
    - Dockerイメージ
    - Open Container Initiative (OCI) イメージ
    - OCI 互換アーティファクト
- イメージリポジトリにアクセスできるユーザーを制御するアクセス許可を設定できる

### プライベートリポジトリの概念
> デフォルトでは、アカウントにはデフォルトリポジトリ (aws_account_id.dkr.ecr.region.amazonaws.com) 内のリポジトリへの読み取りおよび書き込みアクセス権があります。ただし、ユーザーには、Amazon ECR API への呼び出しと、リポジトリに対するイメージのプッシュまたはプルを行う許可が必要です。Amazon ECR には、さまざまなレベルでユーザーアクセスを制御するための管理ポリシーが複数用意されています。詳細については、「Amazon Elastic Container Registry のアイデンティティベースのポリシーの例」を参照してください。

> リポジトリは、 ユーザーアクセスポリシーと個々のリポジトリポリシーによって制御できます。詳細については、「Amazon ECR のプライベートリポジトリポリシー」を参照してください。

> リポジトリ名では、似たリポジトリをグループ化するのに使用できる名前空間をサポートできます。たとえば、同じレジストリを使用するチームが複数ある場合、チーム A が team-a 名前空間を使用し、チーム B が team-b 名前を使用することが可能です。こうすることで、各チームは、web-app という独自のイメージを持つことができます。各イメージの先頭には、チームの名前空間が付けられます。この設定により、各チームは干渉することなくイメージを同時に使用できます。チーム A のイメージは team-a/web-app で、チーム B のイメージは team-b/web-app です。

- 名前空間をサポートしている
- 例）
    - チーム A のイメージは team-a/web-app
    - チーム B のイメージは team-b/web-app 

> イメージは、独自のレジストリ内およびアカウント間でリージョン間で他のリポジトリにレプリケートできます。これを行うには、レジストリ設定でレプリケーション構成を指定します。詳細については、「Amazon ECR のプライベートレジストリ設定」を参照してください。

## Amazon ECR でソフトウェアの脆弱性がないかイメージをスキャンする
> 基本的なスキャン機能が改善された Amazon ECR のプレビューリリースであり、変更される可能性があります。このパブリックプレビューでは、 のみを使用して AWS Management Console 、基本スキャンバージョンの改善をオプトインできます。

- 基本スキャンバージョンの改善版をオプトインにより使用ができる

> Amazon ECR イメージスキャンは、コンテナイメージ内のソフトウェアの脆弱性を特定するのに役立ちます。次のスキャンタイプが提供されています。

> - 拡張スキャン— Amazon ECR は Amazon Inspector と統合され、リポジトリの自動継続的なスキャンを提供します。コンテナイメージは、オペレーティングシステムとプログラミング言語パッケージの両方の脆弱性についてスキャンされます。新しい脆弱性が表示されると、スキャン結果が更新され、Amazon Inspector は にイベントを発行 EventBridge して通知します。拡張スキャンには、次の機能があります。
>   - OS とプログラミング言語は脆弱性をパッケージ化します。
>   - 2 つのスキャン頻度: プッシュスキャンと連続スキャン。
> - 基本スキャン — Amazon ECR は、共通脆弱性識別子 (CVEs) データベースを使用する基本スキャンの 2 つのバージョンを提供します。オープンソースの Clair プロジェクトを使用する現在の GA バージョンと、 AWS ネイティブテクノロジーを使用する新しく改善された基本スキャン (プレビュー) バージョンです。ベーシックスキャンでは、プッシュ時にスキャンするようにリポジトリを設定します。手動スキャンを実行すると Amazon ECR によってスキャン結果のリストが提供されます。ベーシックスキャンには、次の機能があります。
> - OS スキャン。
> - 2 つのスキャン頻度: 手動とプッシュ時のスキャン。


|項目 | 基本スキャン| 拡張スキャン|
|:--|:--|:--|
|特徴|2つのバージョンがある<BR>OSSのClairプロジェクト<BR>・AWSネイティブテクノロジーを使用する|Amazon Inspectorと統合されている。<BR>InspectorからEventBridgeにイベントを発行できる|
|タイミング|・プッシュ時<BR>・手動スキャン|・プッシュ時<BR>・連続スキャン|
|対象|・OSスキャン|・OSスキャン<BR>・プログラミング言語|

## アップストリームレジストリと Amazon ECR プライベートレジストリを同期する
> プルスルーキャッシュルールを使用すると、アップストリームレジストリの内容を Amazon ECR プライベートレジストリと同期できます。
Amazon ECR は現在、次のアップストリームレジストリのプルスルーキャッシュルールの作成をサポートしています。
> - Docker Hub、Microsoft Azure コンテナレジストリ、 GitHub コンテナレジストリ、 GitLab コンテナレジストリ (認証が必要）
> - Amazon ECR パブリック、Kubernetes コンテナイメージレジストリ、Quay (認証は不要)

- プルスルーキャッシュルールにより、アップストリームレジストリをECRプライベートレジストリと同期できる
- サポートしているキャッシュルール
    - Docker Hub
    - Microsoft Azure コンテナレジストリ
    - GitHub コンテナレジストリ
    - GitLab コンテナレジストリ (認証が必要）
    - Amazon ECR パブリック
    - Kubernetes コンテナイメージレジストリ
    - Quay (認証は不要)

> GitLab コンテナレジストリの場合、Amazon ECR は GitLab software-as-a-service offering, GitLab.com でのみプルスルーキャッシュをサポートします。

> 認証が必要なアップストリームレジストリの場合は、認証情報を AWS Secrets Manager シークレットに保存する必要があります。Amazon ECR コンソールでは、認証された各アップストリームレジストリの Secrets Manager シークレットを簡単に作成できます。Secrets Manager コンソールを使用して Secrets Manager シークレットを作成する方法の詳細については、「」を参照してくださいシーク AWS Secrets Manager レットへのアップストリームリポジトリ認証情報の保存。

> アップストリームレジストリのプルスルーキャッシュルールを作成したら、Amazon ECR プライベートレジストリ URI を使用して、そのアップストリームレジストリからイメージをプルするだけです。次に Amazon ECR はリポジトリを作成し、そのイメージをプライベートレジストリにキャッシュします。特定のタグを持つキャッシュされたイメージのその後のプルリクエストで、Amazon ECR はアップストリームレジストリをチェックして、その特定のタグを持つイメージの新しいバージョンがあるかどうかを確認し、少なくとも 24 時間に 1 回プライベートレジストリ内のイメージを更新しようとします。

## Amazon ECR のライフサイクルポリシーを使用してイメージのクリーンアップを自動化する
> Amazon ECR ライフサイクルポリシーを使用すると、プライベートリポジトリ内のイメージのライフサイクル管理をより詳細に制御できます。ライフサイクルポリシーには 1 つ以上のルールが含まれており、各ルールは Amazon ECR のアクションを定義します。ライフサイクルポリシーの有効期限条件に基づいて、イメージは経過時間または 24 時間以内の数に基づいて期限切れになります。Amazon ECR がライフサイクルポリシーに基づいてアクションを実行すると、このアクションは のイベントとしてキャプチャされます AWS CloudTrail。詳細については、「を使用した Amazon ECR アクションのログ記録 AWS CloudTrail」を参照してください。

- プライベートリポジトリ内のイメージのライフサイクル管理を詳細に制御できる
- 1つ以上のルールが含まれている
- ECRのアクションを定義できる
- ライフサイクルポリシーの有効期限条件に基づき、イメージは、24H以内の数に基づき期限切れになる
- アクションは、CloudTrailの記録の対象になる

# 実践
下記の環境を構築します。

## 環境
- ECRと2つのVPCエンドポイントを作成します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/211a49fb-f086-2cd1-40d0-187849857c49.png)


## ファイル構成
- 下記のファイル構成とします。terraform-modules配下に、ecrフォルダを作成します
    ```
    .
    ├── README.md
    ├── backend.hcl
    ├── backend.tf
    ├── main.tf
    ├── provider.tf
    ├── terraform-modules
    │   ├── ecr
    │   │   ├── main.tf
    │   │   ├── outputs.tf
    │   │   └── variables.tf
    │   ├── s3
    │   │   ├── main.tf
    │   │   ├── outputs.tf
    │   │   └── variables.tf
    │   └── vpc
    │       ├── main.tf
    │       ├── outputs.tf
    │       └── variables.tf
    ├── terraform.tfvars
    └── variable.tf
    ```

## 前提
[GitHubActionsとTerraformでNWを作成してみた](https://qiita.com/zumax/items/5a9435d0d7371a8c5033)で作成した環境を前提とします。

- Code Repository:GitHub
- CI/CD:GitHub Actions
- IaC: Terraform
- AWS
    - ap-northeast-1リージョン
    - 1aと1cの2AZ
    - VPCとサブネット構築済み

## IAMポリシーを修正
1. GitHub Actionsから呼び出されるIAMロールのポリシーのStatement に下記のコードを追加します
    ```
        {
            "Sid": "ECR",
            "Effect": "Allow",
            "Action": [
                "ecr:CreateRepository",
                "ecr:ListTagsForResource",
                "ecr:DeleteRepository",
                "ecr:TagResource",
                "ec2:DescribePrefixLists",
                "ec2:CreateVpcEndpoint",
                "ec2:DescribeVpcEndpoints",
                "ec2:DeleteVpcEndpoints",
                "ec2:ModifyVpcEndpoint",
                "ec2:DescribeRouteTables",
                "ec2:DescribeSecurityGroups",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:DescribeImages",
                "ecr:GetRepositoryPolicy",
                "ecr:BatchCheckLayerAvailability",
                "ecr:BatchGetImage",
                "ecr:GetDownloadUrlForLayer",
                "ecr:SetRepositoryPolicy"
            ],
            "Resource": "*"
        }
    ```


## ECRモジュールを作成
1. `terraform-modules`フォルダの配下に、`ecr` フォルダを作成します
1. ecrフォルダに、`main.rf`、`outputs.tf`、`variables.tf`ファイルを追加します
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5979021c-b413-6480-af5b-ea99d54cc5d0.png)

### main.tf
1. 下記のコードを実装します
    ```terraform
    resource "aws_ecr_repository" "my_java_app" {
      name                 = var.ecr_repository_name
      image_tag_mutability = "MUTABLE"
      image_scanning_configuration {
        scan_on_push = true
      }
      encryption_configuration {
        encryption_type = "AES256"
      }
    
      tags = merge(var.tags, { "Name" = format("%s-ecr-Repository-MyJavaApp", var.project_name) })
    }
    
    resource "aws_vpc_endpoint" "ecr_dkr" {
      vpc_id             = var.vpc_id
      service_name       = "com.amazonaws.ap-northeast-1.ecr.dkr"
      vpc_endpoint_type  = "Interface"
      subnet_ids         = var.private_subnets
      security_group_ids = [aws_security_group.vpc_endpoint_sg.id]
    
      tags = merge(var.tags, { "Name" = format("%s-ec2-VpcEndpoint-EcrDkr", var.project_name) })
    }
    
    resource "aws_vpc_endpoint" "ecr_api" {
      vpc_id             = var.vpc_id
      service_name       = "com.amazonaws.ap-northeast-1.ecr.api"
      vpc_endpoint_type  = "Interface"
      subnet_ids         = var.private_subnets
      security_group_ids = [aws_security_group.vpc_endpoint_sg.id]
    
      tags = merge(var.tags, { "Name" = format("%s-ec2-VpcEndpoint-EcrAPI", var.project_name) })
    }
    
    resource "aws_security_group" "vpc_endpoint_sg" {
      name        = "vpc-endpoint-sg"
      description = "Security group for VPC endpoints"
      vpc_id      = var.vpc_id
    
      ingress {
        from_port   = 443
        to_port     = 443
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      tags = merge(var.tags, { "Name" = format("%s-ec2-SecurityGroup-ECR", var.project_name) })
    }
    
    resource "aws_ecr_repository_policy" "my_java_app_policy" {
      repository = aws_ecr_repository.my_java_app.name
    
      policy = jsonencode({
        Version = "2012-10-17",
        Statement = [
          {
            Effect = "Allow",
            Principal = {
              "AWS" : "arn:aws:iam::${var.account_id}:root"
            },
            Action = [
              "ecr:GetDownloadUrlForLayer",
              "ecr:BatchGetImage",
              "ecr:BatchCheckLayerAvailability"
            ],
            Condition = {
              StringEquals : {
                "aws:sourceVpce" : aws_vpc_endpoint.ecr_dkr.id
              }
            }
          },
          {
            Effect = "Allow",
            Principal = {
              "AWS" : "arn:aws:iam::${var.account_id}:role/${var.iam_role}"
            },
            Action = [
              "ecr:GetDownloadUrlForLayer",
              "ecr:BatchGetImage",
              "ecr:BatchCheckLayerAvailability",
              "ecr:PutImage"
            ]
          }
        ]
      })
    }

    ```
### variables.tf
1. 下記のコードを実装します
    ```terraform
    variable "project_name" {
      description = "The name of the project"
      type        = string
    }
    
    variable "account_id" {
      description = "The AWS account ID"
      type        = string
    }
    
    variable "ecr_repository_name" {
      description = "The name of the ECR repository"
      type        = string
    }
    
    variable "vpc_id" {
      description = "The ID of the VPC"
      type        = string
    }
    
    variable "private_subnets" {
      description = "A list of private subnet IDs"
      type        = list(string)
    }
    
    variable "tags" {
      description = "A map of tags to assign to the resource"
      type        = map(string)
      default     = {}
    }
    
    variable "iam_role" {
      description = "The IAM role for ECR access"
      type        = string
    }

    ```

### outputs.tf
1. 下記のコードを実装します
    ```terraform
    output "ecr_repository_url" {
      value = aws_ecr_repository.my_java_app.repository_url
    }
    
    output "ecr_repository_name" {
      value = aws_ecr_repository.my_java_app.name
    }
    
    output "vpc_endpoint_ids" {
      value = [
        aws_vpc_endpoint.ecr_dkr.id,
        aws_vpc_endpoint.ecr_api.id
      ]
    }
    
    output "vpc_endpoint_sg_id" {
      value = aws_security_group.vpc_endpoint_sg.id
    }
    ```
## vpcモジュールの修正
### main.tf
1. 下記のコードを変更します
    - enable_dns_support
    - enable_dns_hostnames
    ```terraform
    resource "aws_vpc" "this" {
      cidr_block           = var.vpc_cidr
      enable_dns_support   = true
      enable_dns_hostnames = true
      tags                 = merge(var.tags, { "Name" = format("%s-vpc", var.project_name) })
    }
    ```

### outputs.tf
1. 下記のコードに変更します
    ```terraform
    output "vpc_id" {
      description = "The ID of the VPC"
      value       = aws_vpc.this.id
    }
    
    output "public_subnet_ids" {
      description = "The IDs of the public subnets"
      value       = aws_subnet.public[*].id
    }
    
    output "private_subnet_ids" {
      description = "The IDs of the private subnets"
      value       = aws_subnet.private[*].id
    }
    
    output "db_subnet_ids" {
      description = "The IDs of the DB subnets"
      value       = aws_subnet.db[*].id
    }
    ```

## ルートモジュールを作成
### main.tf
1. 下記のコードを追加します
    ```terraform
    module "ecr" {
      source = "./terraform-modules/ecr"
    
      account_id          = data.aws_caller_identity.this.account_id
      ecr_repository_name = var.ecr_repository_name
      vpc_id              = module.vpc.vpc_id
      private_subnets     = module.vpc.private_subnet_ids
      tags                = var.ecr_tags
      iam_role            = var.infra_iam_role
      project_name        = var.project_name
    }    
    ```
### variable.tf
1. 下記のコードを追加します
    ```terraform
    variable "ecr_repository_name" {
      description = "The name of the ECR repository"
      type        = string
    }
    
    variable "ecr_tags" {
      description = "A map of tags to assign to the resource"
      type        = map(string)
      default     = {}
    }
    ```

## 確認
1. Commit & Pushします
1. 成功しました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/83a230a9-8fa0-ac95-8dbe-2c4a405de33c.png)

### ECR
1. AWSにサインインします
1. Elastic Container Registryに移動します
1. ナビゲーションペインで、Repositories をクリックします。できていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/3c8e8fb0-1c46-af8f-c73e-12d7d9eb1143.png)

### VPCエンドポイント
1. VPC に移動します
1. ナビゲーションペインで、エンドポイント をクリックします。できていました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b468657a-8f32-a222-cb37-e79ed7c806a5.png)


# 考察
今回、ECRについて、機能や特徴についてあらためて整理しました。
次回は、ECS/Fargate上で動作するアプリケーションについて、試してみます。

# 参考
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/what-is-ecr.html

https://aws.amazon.com/jp/ecr/pricing/