# Terraform で Device Protection をデプロイする

Terraform はマシンを作成するツールであり、既存のマシン上でソフトウェアの状態を収束させ続けるものではありません。そのため、ここでの Terraform の役割は、作成するすべての Linux インスタンスが Device Protection をインストール済みの状態で起動するようにすることです。

まず [Install Device Protection for Linux](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/install-device-protection-for-linux) を読み、すべての方法で共通して使用するトークン、パッケージ、インストールオプションを確認してください。トークンの取り扱い、再起動、繰り返し実行、デバイス ID などその他の事項は [Linux Rollout Reference](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/linux-rollout-reference) で説明しています。

{% hint style="info" %}
これは [Aikido Terraform provider](https://help.aikidogov.us/docs/docs-ja/miscellaneous-integrations/aikido-terraform-provider) とは別物です。このプロバイダーは、リポジトリの有効化や AutoFix 設定など、Aikido ワークスペースの構成を管理するものです。マシンへのエージェントのインストールとは関係ありません。
{% endhint %}

## 新しいインスタンスにインストールする

cloud-init ドキュメントを `user_data` として渡します。ドキュメントを独立したファイルにしておくと、読みやすさが保たれ、Terraform 以外でも再利用できます。ドキュメント自体とそのオプションについては [cloud-init](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/deploy-device-protection-with-cloud-init) を参照してください。

{% code title="main.tf" %}
```terraform
variable "aikido_token" {
  type      = string
  sensitive = true
}

resource "aws_instance" "workstation" {
  ami           = var.workstation_ami
  instance_type = "m7i.large"

  user_data = templatefile("${path.module}/aikido-user-data.yaml.tftpl", {
    package      = "EndpointProtection-amd64.deb"
    aikido_token = var.aikido_token
  })

  tags = {
    Name = "workstation"
  }
}
```
{% endcode %}

{% code title="aikido-user-data.yaml.tftpl" overflow="wrap" %}
```yaml
#cloud-config
runcmd:
  - curl -fsSLo /tmp/${package} https://github.com/AikidoSec/safechain-internals/releases/latest/download/${package}
  - AIKIDO_TOKEN=${aikido_token} apt-get install -y /tmp/${package}
  - rm -f /tmp/${package}
```
{% endcode %}

{% hint style="info" %}
cloud-init の `runcmd` は初回起動時にのみ実行されるため、`user_data` を変更しても、すでに稼働中のインスタンスには何も再インストールされません。変更が反映されるのは、変更後に作成されたインスタンスのみです。
{% endhint %}

### トークンを state とメタデータに残さない

Terraform が `user_data` にレンダリングした内容はすべて state に保存されます。また、`user_data` はマシン上で実行されているあらゆるプロセスからインスタンスメタデータサービス経由で読み取れます。トークンの代わりにシークレットの名前を渡し、ブートスクリプトに値を取得させてください。

{% code title="main.tf" %}
```terraform
  user_data = templatefile("${path.module}/aikido-user-data.yaml.tftpl", {
    package         = "EndpointProtection-amd64.deb"
    token_parameter = "/aikido/device-protection-token"
  })
```
{% endcode %}

インスタンスプロファイルにそのパラメータへの読み取り権限を付与し、`aikido-user-data.yaml.tftpl` で [boot-time lookup](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/linux-rollout-reference#fetch-the-token-at-boot-instead) を使用します。その際、ハードコードされたパラメータ名とパッケージファイルの代わりに `${token_parameter}` と `${package}` を使います。これにより `aikido_token` 変数は完全に不要になり、機密情報が Terraform の state に入ることはなくなります。

## Terraform で管理済みのインスタンスにインストールする

作成時に一度だけプッシュする場合は、`remote-exec` プロビジョナーを使用します。

{% code overflow="wrap" %}
```terraform
resource "aws_instance" "workstation" {
  # ...

  connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file(var.ssh_private_key)
  }

  provisioner "remote-exec" {
    inline = [
      "curl -fsSLo /tmp/EndpointProtection-amd64.deb https://github.com/AikidoSec/safechain-internals/releases/latest/download/EndpointProtection-amd64.deb",
      "sudo AIKIDO_TOKEN=${var.aikido_token} apt-get install -y /tmp/EndpointProtection-amd64.deb",
      "rm -f /tmp/EndpointProtection-amd64.deb",
    ]
  }
}
```
{% endcode %}

プロビジョナーには実際的な制約があり、そのため HashiCorp はこれを最後の手段と位置づけています。

* 作成時に一度だけ実行されます。二度と実行されないため、ドリフトが修正されることはありません。
* プロビジョナーが失敗するとリソースは tainted としてマークされ、次回の apply でインスタンスが破棄・再作成されます。
* Terraform を実行する場所(CI を含む)から SSH で到達可能である必要があります。

{% hint style="info" %}
既存のフリートには、[Ansible](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/deploy-device-protection-with-ansible)、[Chef](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/deploy-device-protection-with-chef)、または [Puppet](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/deploy-device-protection-with-puppet) を使用してください。AWS では、稼働中のインスタンスを操作するために設計された [SSM Run Command and State Manager](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/deploy-device-protection-with-aws-systems-manager) も Terraform より適しています。
{% endhint %}

## ロールアウトを確認する

新しいインスタンス上で次を実行します。

{% code overflow="wrap" %}
```bash
cloud-init status --wait
systemctl is-active aikido-endpoint-protection
aikidoctl version
```
{% endcode %}

その後、デバイスが [デバイス一覧](https://app.aikido.dev/endpoint-protection/devices) に **Active** ステータスで表示されます。

## トラブルシューティング


| 問題                                                                                  | 対処法                                                                                                                                                                                                                                             |
| ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| パッケージはインストールされるが、デバイスがダッシュボードに表示されない                       | インストールコマンドに `AIKIDO_TOKEN` が設定されていませんでした。構成管理ツールはローカル環境をホストに転送しないため、タスク自体に変数を設定してから、パッケージを再度インストールしてください                                    |
| `apt` または `dnf` がファイルをインストールせず、リポジトリ内でパッケージを探してしまう | 名前ではなくパスを指定してください: `apt install ./EndpointProtection-amd64.deb`。先頭の `./` によって、パッケージマネージャーはそれをローカルファイルとして扱います                                                                                              |
| Red Hat 系のホストでインストールが失敗する                                               | ビルドをメジャーバージョンに合わせてください。バージョン 9 では `el9` パッケージを、バージョン 10 では `el10` パッケージを使用します                                                                                                                                   |
| サービスが実行されていない                                                               | `systemctl status aikido-endpoint-protection` を実行し、次に `sudo aikidoctl diagnostics` を実行して詳細を送信してください                                                                                                                                 |
| Node.js または uv が依然として Aikido 証明書を拒否する                                        | インストール時に `NODE_EXTRA_CA_CERTS` と `UV_SYSTEM_CERTS` がシステム全体に設定されますが、実行中のシェルにはこれらが反映されません。新しいログインシェルを開くか、デバイスを再起動してください                                                                                    |
| トークンが実行出力やログに表示される                                                 | ツールのマスキング機能を使用してください: Ansible では `no_log`、Chef では `sensitive true`、Puppet では `Sensitive()`                                                                                                                                               |
| 複数のマシンがデバイス一覧で 1 つのエントリを共有している                                      | それらのマシンは同じ `/etc/machine-id` で起動しています。Linux のデバイス ID はこのファイルに基づくため、クローン元のイメージでこのファイルをクリアし、初回起動時に systemd に新しいものを書き込ませてください                                                                |
| トレイアイコンが表示されない                                                                     | Ubuntu 以外の GNOME では想定どおりの動作であり、見た目上の問題にすぎません。[Tray Icon Support on Linux](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/install-device-protection-for-linux/tray-icon-support-on-linux) を参照してください |
