AWS CodeArtifactをTerraformで構築する（＋Mavenから利用）

# What's

* Terraform AWS Providerが3.9.0からAWS CodeArtifactに対応した
    * https://github.com/terraform-providers/terraform-provider-aws/releases/tag/v3.9.0
    * 3.9.0でドメイン、リポジトリ、ドメインパーミッション
    * 3.10.0でリポジトリパーミッションポリシー、認証トークン
    * 3.11.0でエンドポイント
    * と対応の幅も広がり中

というわけで、TerraformでAWS CodeArtifactを構築してみたいと思います。

以前に書いたこちらの内容を、Terraformで行ってみようかな、と。

[AWS CodeArtifactをMavenから使ってみる](https://qiita.com/charon/items/057da67e810a0ba96fed)

AWS CodeArtifactの基本的な説明はこちらの記事にそこそこ書いたので、今回はそのあたりは簡単にしておきます。


# AWS CodeArtifactとは？

AWSの提供する、マネージドなアーティファクトリポジトリサービスです。

現時点でサポートしているパッケージフォーマットは、npm、PyPI、Mavenです。


# 始め方

ざっくり言うと、

* AWS KMS CMKを作る（AWS管理のものでも可）
* ドメインを作る
* ドメイン内にリポジトリを作る

という感じです。

現時点（3.11.0）のTerraform AWS Providerでは、外部リポジトリへの接続はできません。

[r/codeartifact_repository - support external connection #15569](https://github.com/terraform-providers/terraform-provider-aws/pull/15569)

前回の記事に加えて、Maven Centralへの接続も試そうかと思ったのですが、今回は諦めました…。


# リポジトリを構築してみる

では、リポジトリを構築してみましょう。今回の環境は、こちらです。

```shell
$ terraform version
Terraform v0.13.4
+ provider registry.terraform.io/hashicorp/aws v3.11.0


$ aws --version
aws-cli/2.0.58 Python/3.7.3 Linux/5.4.0-52-generic exe/x86_64.ubuntu.20
```

AWSのクレデンシャルは、環境変数で設定しているものとします。

```shell
$ export AWS_ACCESS_KEY_ID=.....
$ export AWS_SECRET_ACCESS_KEY=.....
$ export AWS_DEFAULT_REGION=.....
```

リソース定義。

`main.tf`

```go
terraform {
  required_version = "0.13.4"

  required_providers {
    aws = {
      version = "3.11.0"
      source  = "hashicorp/aws"
    }
  }
}

provider "aws" {}

resource "aws_kms_key" "codeartifact_key" {
  description             = "CMK for My CodeArtifact Domain"
  deletion_window_in_days = 7
}

resource "aws_kms_alias" "codeartifact_key" {
  name          = "alias/my-codeartifact-key"
  target_key_id = aws_kms_key.codeartifact_key.key_id
}

resource "aws_codeartifact_domain" "domain" {
  domain         = "my-domain"
  encryption_key = aws_kms_alias.codeartifact_key.arn
}

resource "aws_codeartifact_repository" "my_repository" {
  repository = "my-repo"
  domain     = aws_codeartifact_domain.domain.domain
}
```

AWS KMS CMK。エイリアスも、なんとなく作っておきました。

```go
resource "aws_kms_key" "codeartifact_key" {
  description             = "CMK for My CodeArtifact Domain"
  deletion_window_in_days = 7
}

resource "aws_kms_alias" "codeartifact_key" {
  name          = "alias/my-codeartifact-key"
  target_key_id = aws_kms_key.codeartifact_key.key_id
}
```

AWS CodeArtifactのドメインとリポジトリ。ドメイン内に、リポジトリが含まれる形になります。

[Resource: aws_codeartifact_domain](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codeartifact_domain)

[Resource: aws_codeartifact_repository](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codeartifact_repository)

```go
resource "aws_codeartifact_domain" "domain" {
  domain         = "my-domain"
  encryption_key = aws_kms_alias.codeartifact_key.arn
}

resource "aws_codeartifact_repository" "my_repository" {
  repository = "my-repo"
  domain     = aws_codeartifact_domain.domain.domain
}
```

CMKは、ドメインに対して設定します。

権限まわりの設定も可能なようですが、今回はいったんパス。

[Resource: aws_codeartifact_domain_permissions_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codeartifact_domain_permissions_policy)

[Resource: aws_codeartifact_repository_permissions_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codeartifact_repository_permissions_policy)

では、`apply`してみます。

```shell
$ terraform init
$ terraform apply
```

確認してみましょう。

```shell
$ aws codeartifact describe-domain --domain my-domain
{
    "domain": {
        "name": "my-domain",
        "owner": "[AWSアカウントID]",
        "arn": "arn:aws:codeartifact:ap-northeast-1:[AWSアカウントID]:domain/my-domain",
        "status": "Active",
        "createdTime": "2020-10-21T17:04:12.372000+09:00",
        "encryptionKey": "arn:aws:kms:ap-northeast-1:[AWSアカウントID]:key/fa1a0cf2-5534-467e-bd56-f034a3318d3c",
        "repositoryCount": 1,
        "assetSizeBytes": 0
    }
}


$ aws codeartifact describe-repository --domain my-domain --repository my-repo
{
    "repository": {
        "name": "my-repo",
        "administratorAccount": "[AWSアカウントID]",
        "domainName": "my-domain",
        "domainOwner": "[AWSアカウントID]",
        "arn": "arn:aws:codeartifact:ap-northeast-1:[AWSアカウントID]:repository/my-domain/my-repo",
        "upstreams": [],
        "externalConnections": []
    }
}
```

OKですね。とても簡単にできました。


# Mavenから使う

最後に、Apache Mavenから使ってみましょう。

[Using CodeArtifact with Maven
](https://docs.aws.amazon.com/codeartifact/latest/ug/using-maven.html)

内容は、前回の記事と同様に

* ライブラリを作って、AWS CodeArtifactリポジトリにアップロード
* ライブラリを使うアプリケーションを作って、依存ライブラリをAWS CodeArtifactリポジトリからダウンロード

という感じでやってみます。

環境については、こちら。

```shell
$ java --version
openjdk 11.0.8 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.8, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-52-generic", arch: "amd64", family: "unix"
```


## ライブラリ側

まずは、ライブラリを作ります。

AWS CodeArtifactリポジトリへのアップロードが必要になるので、ドキュメントに従い`settings.xml`を作成しましょう。

`settings.xml`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
    <servers>
        <server>
            <id>codeartifact</id>
            <username>aws</username>
            <password>${env.CODEARTIFACT_TOKEN}</password>
        </server>
    </servers>
</settings>
```

次に、認証トークンを取得します。

```shell
$ export CODEARTIFACT_TOKEN=`aws codeartifact get-authorization-token --domain my-domain --query authorizationToken --output text`
```

認証トークンはTerraformでも取得可能なようですが、環境変数に設定しなくてはいけないので、ここはAWS CLIでよいでしょう。

参考までに、Terraformで扱う場合はこちらですね。

[Data Source: aws_codeartifact_authorization_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/codeartifact_authorization_token)

認証トークンの有効期限は、12時間です。

[AWS CodeArtifact authentication and tokens](https://docs.aws.amazon.com/codeartifact/latest/ug/tokens-authentication.html)


### アーティファクトをリポジトリにアップロードする

ここまで揃ったら、ライブラリを作成してアーティファクトをAWS CodeArtifactリポジトリにアップロードします。

`pom.xml`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.charon.r13b</groupId>
    <artifactId>library</artifactId>
    <version>0.0.1</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>codeartifact</id>
            <name>codeartifact</name>
            <url>https://my-domain-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/my-repo/</url>
        </repository>
    </repositories>

    <distributionManagement>
        <repository>
            <id>codeartifact</id>
            <name>codeartifact</name>
            <url>https://my-domain-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/my-repo</url>
        </repository>
    </distributionManagement>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.11</version>
        </dependency>
    </dependencies>
</project>
```

今回の使い方だと、少なくとも`distributionManagement`にリポジトリ指定があればOKです。

一応、Maven Centralから取得するライブラリも含めています。

ソースコード。

`src/main/java/com/github/charon/r13b/library/MessageService.java`

```java
package com.github.charon.r13b.library;

import org.apache.commons.lang3.StringUtils;

public class MessageService {
    String prefix;
    String suffix;

    public MessageService(String prefix, String suffix) {
        this.prefix = prefix;
        this.suffix = suffix;
    }

    public String decorate(String word) {
        return StringUtils.join(prefix, word, suffix);
    }
}
```

ビルドして、アップロードしてみます。

```shell
$ mvn deploy -s settings.xml
```

OKです。

```shell
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  9.789 s
[INFO] Finished at: 2020-10-21T17:32:55+09:00
[INFO] ------------------------------------------------------------------------
```


### 呼び出し側を作る

アップロードされたアーティファクトを使用する、アプリケーションを作成します。

`pom.xml`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.charon.r13b</groupId>
    <artifactId>app</artifactId>
    <version>0.0.1</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>codeartifact</id>
            <name>codeartifact</name>
            <url>https://my-domain-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/my-repo/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>com.github.charon.r13b</groupId>
            <artifactId>library</artifactId>
            <version>0.0.1</version>
        </dependency>
    </dependencies>
</project>
```

こちらは、`repositories / repository`に、構築したAWS CodeArtifactリポジトリを指定すればOKです。

なお、リポジトリの利用にも認証トークンは必要となります。

アプリケーションのソースコード。

`src/main/java/com/github/charon/r13b/app/App.java`

```java
package com.github.charon.r13b.app;

import com.github.charon.r13b.library.MessageService;

public class App {
    public static void main(String... args) {
        MessageService messageService = new MessageService("★★★", "★★★");
        System.out.println(messageService.decorate("Hello World!!"));
    }
}
```

実行。`-s`で指定しているのは、アーティファクトをアップロードした時に使ったものと同じ、AWS CodeArtifactへの認証情報を書いたものです。

```shell
mvn compile exec:java -s settings.xml -Dexec.mainClass=com.github.charon.r13b.app.App
```

結果。

```shell
[INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ app ---
★★★Hello World!!★★★
```

OKですね。

これで確認完了です。

最後に、リソースを破棄しておしましです。

```shell
$ terraform destroy
```