English

Deploy Device Protection with Terraform

Terraform creates machines, it does not keep software converged on machines that already exist. So Terraform's job here is to make sure every Linux instance it creates comes up with Device Protection already installed.

Read Install Device Protection for Linux first for the token, the packages, and the install options that every method uses. The Linux Rollout Reference covers the rest: token handling, reboots, repeat runs, and device identity.

This is not the Aikido Terraform provider. That provider manages Aikido workspace configuration, such as repository activation and AutoFix settings. It has nothing to do with installing the agent on a machine.

Install on new instances

Pass a cloud-init document as user_data. Keep the document in its own file so it stays readable and can be reused outside Terraform. See cloud-init for the document itself and its options.

main.tf
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"
  }
}
aikido-user-data.yaml.tftpl
#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}

Changing user_data does not reinstall anything on instances that are already running, because cloud-init's runcmd only fires on first boot. A change only affects instances created after it.

Keep the token out of state and metadata

Anything Terraform renders into user_data is stored in state, and user_data is readable from the instance metadata service by anything running on the machine. Pass the name of a secret instead of the token, and let the boot script fetch the value:

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

Grant the instance profile read access to that parameter, and use the boot-time lookup in aikido-user-data.yaml.tftpl, with ${token_parameter} and ${package} in place of the hardcoded parameter name and package file. The aikido_token variable then drops out entirely, so nothing sensitive reaches Terraform state.

Install on instances Terraform already manages

For a one-off push at create time, use a remote-exec provisioner:

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",
    ]
  }
}

Provisioners come with real limits, which is why HashiCorp calls them a last resort:

  • They run once, at create time. They never run again, so drift is never corrected.
  • A failed provisioner marks the resource tainted, and the next apply destroys and recreates the instance.
  • Terraform needs SSH reachability from wherever you run it, including from CI.

For a fleet that already exists, use Ansible, Chef, or Puppet. On AWS, SSM Run Command and State Manager also fit better than Terraform, because they are built to act on running instances.

Verify the rollout

On a new instance:

cloud-init status --wait
systemctl is-active aikido-endpoint-protection
aikidoctl version

The device then appears in your device list with an Active status.

Troubleshooting

ProblemFix
The package installs but the device never appears in the dashboardAIKIDO_TOKEN was not set for the install command. Configuration management tools do not forward your local environment to the host, so set the variable on the task itself, then install the package again
apt or dnf looks for the package in your repositories instead of installing the filePass a path, not a name: apt install ./EndpointProtection-amd64.deb. The leading ./ is what makes the package manager treat it as a local file
The install fails on a Red Hat-family hostMatch the build to the major version: use the el9 package on version 9 and the el10 package on version 10
The service is not runningRun systemctl status aikido-endpoint-protection, then sudo aikidoctl diagnostics to send us the details
Node.js or uv still reject the Aikido certificateInstalling sets NODE_EXTRA_CA_CERTS and UV_SYSTEM_CERTS system-wide, and running shells do not pick them up. Open a new login shell or reboot the device
The token shows up in run output or logsUse your tool's redaction: no_log in Ansible, sensitive true in Chef, Sensitive() in Puppet
Several machines share one entry in the device listThey booted with the same /etc/machine-id. Device identity on Linux follows that file, so clear it in the image you clone from and let systemd write a fresh one on first boot
No tray icon appearsExpected on GNOME outside Ubuntu, and cosmetic. See Tray Icon Support on Linux