← Back to list

Harbor robot accounts with Terraform

Scenario

FrunzaSamuel · 2026-05-07 15:58 · 0 claps · 4.4 min read
#devops #terraform #harbor #docker #docker-compose
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Harbor robot accounts with Terraform

Scenario

If you have a Harbor installation, how to you set up robot accounts? Since Terraform has a provider for Harbor this should be done as infrastructure as code.

Prerequisites

A Linux or MacOS machine for local development. If you are running Windows, you first need to set up the Windows Subsystem for Linux (WSL) environment.

You need docker cli on your machine for testing purposes, and/or on the machines that run your pipeline. You can verify this by running the following command:

docker - version

Set the following environment variable for Harbor access:

  • HARBOR_URL
  • HARBOR_USERNAME
  • HARBOR_PASSWORD

Implementation

We want to run everything in a docker container, so let’s create the dockerfile:

FROM hashicorp/terraform:1.5.0

# Copy scripts to the expected location
COPY ./scripts /app
# Copy terraform to the expected location
COPY ./terraform /app/terraform

Let’s run the terraform commands in a docker compose file:

services:
  main:
    image: harborrobotaccounts
    network_mode: host
    working_dir: /app
    environment:
      - HARBOR_URL=${HARBOR_URL}
      - HARBOR_USERNAME=${HARBOR_USERNAME}
      - HARBOR_PASSWORD=${HARBOR_PASSWORD}
      - ADMIN1_USER_PASSWORD=${ADMIN1_USER_PASSWORD}
      - GUEST1_USER_PASSWORD=${GUEST1_USER_PASSWORD}
      - MYPROJECT_SCANNER_ROBOT_SECRET=${MYPROJECT_SCANNER_ROBOT_SECRET}
      - MYPROJECT_ADMIN_ROBOT_SECRET=${MYPROJECT_ADMIN_ROBOT_SECRET}
      - MYPROJECT_PULL_ROBOT_SECRET=${MYPROJECT_PULL_ROBOT_SECRET}
      - MYPROJECT_MYPROJECT2_SYSTEM_ADMIN_ROBOT_SECRET=${MYPROJECT_MYPROJECT2_SYSTEM_ADMIN_ROBOT_SECRET}
    entrypoint: ["sh", "-c"]
    command: ["sh updateVariables.sh && terraform init && terraform validate && terraform apply -auto-approve"]

Here we also pass more environment variables used for various purposes: the first 3 are needed as credentials for the Harbor provider, and the rest are used to replace placeholders secrets for various Harbor accounts. The replacement is done by the updateVariables.sh script:

#!/bin/sh

# Exit immediately if a simple command exits with a nonzero exit value
set -e

sed -i "s/_ADMIN1_USER_PASSWORD_/${ADMIN1_USER_PASSWORD}/" ./users.tf
echo "Replacement of ADMIN1_USER_PASSWORD done"

sed -i "s/_GUEST1_USER_PASSWORD_/${GUEST1_USER_PASSWORD}/" ./users.tf
echo "Replacement of GUEST1_USER_PASSWORD done"

sed -i "s/_MYPROJECT_SCANNER_ROBOT_SECRET_/${MYPROJECT_SCANNER_ROBOT_SECRET}/" ./robotAccounts.tf
echo "Replacement of MYPROJECT_SCANNER_ROBOT_SECRET done"

sed -i "s/_MYPROJECT_ADMIN_ROBOT_SECRET_/${MYPROJECT_ADMIN_ROBOT_SECRET}/" ./robotAccounts.tf
echo "Replacement of MYPROJECT_ADMIN_ROBOT_SECRET done"

sed -i "s/_MYPROJECT_PULL_ROBOT_SECRET_/${MYPROJECT_PULL_ROBOT_SECRET}/" ./robotAccounts.tf
echo "Replacement of MYPROJECT_PULL_ROBOT_SECRET done"

sed -i "s/_MYPROJECT_MYPROJECT2_SYSTEM_ADMIN_ROBOT_SECRET_/${MYPROJECT_MYPROJECT2_SYSTEM_ADMIN_ROBOT_SECRET}/" ./robotAccounts.tf
echo "Replacement of MYPROJECT_MYPROJECT2_SYSTEM_ADMIN_ROBOT_SECRET done"

Now comes the Terraform part. First of all, we must configure the Harbor provider:

# terraform/providers.tf
terraform {
  required_providers {
    harbor = {
      source  = "goharbor/harbor"
      version = "3.10.15"
    }
  }
}

# Configure the Harbor Provider
# Credentials can be provided by using the HARBOR_URL, HARBOR_USERNAME and HARBOR_PASSWORD environment variables. (https://registry.terraform.io/providers/goharbor/harbor/latest/docs)
provider "harbor" {
}

Let’s start by creating some Harbor projects:

# terraform/projects.tf
resource "harbor_project" "myProject" {
  name                   = "myProject"
  public                 = true         # (Optional) Default value is false
  vulnerability_scanning = false        # (Optional) Default value is true. Automatically scan images on push
}

resource "harbor_project" "myProject2" {
  name                   = "myProject2"
  public                 = true         # (Optional) Default value is false
  vulnerability_scanning = false        # (Optional) Default value is true. Automatically scan images on push
}

Let’s also create some users:

# terraform/users.tf
resource "harbor_user" "admin1User" {
 username = "admin1"
 password = "_ADMIN1_USER_PASSWORD_"
 full_name = "admin1"
 email = "admin1@mycompany.com"
}

resource "harbor_user" "guest1User" {
 username = "guest1"
 password = "_GUEST1_USER_PASSWORD_"
 full_name = "guest1"
 email = "guest1@mycompany.com"
}

, and map them to the projects:

# terraform/projectMembers.tf
resource "harbor_project_member_user" "adminMyProjectAdmin1" {
 project_id    = harbor_project.myProject.id 
 user_name     = harbor_user.admin1User.username
 role          = "projectadmin" 
}

resource "harbor_project_member_user" "guestMyProjectGuest1" {
 project_id    = harbor_project.myProject.id 
 user_name     = harbor_user.guest1User.username
 role          = "guest"
}

Usernames reflect their assigned roles to make the mapping easier to understand. They have some number suffixes in their name so that you understand how to easily add more.

Since we also want some programmatic access, let’s get now to the robot accounts:

# terraform/robotAccounts.tf
resource "harbor_robot_account" "myProjectScanningRobotAccount" {
  name        = "scanning-robot-account-myproject"
  description = "MyProject scanning robot account"
  level       = "project"
  secret      = "_MYPROJECT_SCANNER_ROBOT_SECRET_"
  permissions {
    access {
      action   = "create"
      resource = "scan"
    }
    access {
      action   = "create"
      resource = "sbom"
    }
    access {
      action   = "list"
      resource = "artifact"
    }
    access {
      action   = "list"
      resource = "log"
    }
    access {
      action   = "list"
      resource = "metadata"
    }
    access {
      action   = "list"
      resource = "repository"
    }
    access {
      action   = "list"
      resource = "tag"
    }
    access {
      action   = "pull"
      resource = "repository"
    }
    access {
      action   = "read"
      resource = "artifact"
    }
    access {
      action   = "read"
      resource = "metadata"
    }
    access {
      action   = "read"
      resource = "project"
    }
    access {
      action   = "read"
      resource = "repository"
    }
    access {
      action   = "read"
      resource = "quota"
    }
    access {
      action   = "read"
      resource = "sbom"
    }
    access {
      action   = "read"
      resource = "scan"
    }
    kind      = "project"
    namespace = harbor_project.myProject.name
  }
}

resource "harbor_robot_account" "myProjectAdminRobotAccount" {
  name        = "myprojectadmin"
  description = "MyProject admin user that can push and pull"
  level       = "project"
  secret      = "_MYPROJECT_ADMIN_ROBOT_SECRET_"
  permissions {
    access {
      action   = "push"
      resource = "repository"
    }
    access {
      action   = "pull"
      resource = "repository"
    }
    kind      = "project"
    namespace = harbor_project.myProject.name
  }
}

resource "harbor_robot_account" "myProjectPullRobotAccount" {
  name        = "myprojectpull"
  description = "MyProject user that can pull"
  level       = "project"
  secret      = "_MYPROJECT_PULL_ROBOT_SECRET_"
  permissions {
    access {
      action   = "pull"
      resource = "repository"
    }
    kind      = "project"
    namespace = harbor_project.myProject.name
  }
}

resource "harbor_robot_account" "systemAminRobotAccountMyProjectMyProject2" {
  name        = "systemadminmyprojectmyproject2"
  description = "System admin user that can push and pull in MyProject and MyProject2"
  level       = "system"
  secret      = "_MYPROJECT_MYPROJECT2_SYSTEM_ADMIN_ROBOT_SECRET_"
  permissions {
    access {
      action   = "create"
      resource = "label"
    }
    kind      = "project"
    namespace = harbor_project.myProject.name
  }
  permissions {
    access {
      action   = "push"
      resource = "repository"
    }
    kind      = "project"
    namespace = harbor_project.myProject.name
  }
  permissions {
    access {
      action   = "pull"
      resource = "repository"
    }
    kind      = "project"
    namespace = harbor_project.myProject.name
  }
  permissions {
    access {
      action   = "create"
      resource = "label"
    }
    kind      = "project"
    namespace = harbor_project.myProject2.name
  }
  permissions {
    access {
      action   = "push"
      resource = "repository"
    }
    kind      = "project"
    namespace = harbor_project.myProject2.name
  }
  permissions {
    access {
      action   = "pull"
      resource = "repository"
    }
    kind      = "project"
    namespace = harbor_project.myProject2.name
  }
}

Robot accounts is what you usually use most of the time, since I assume you will want to automate as much as possible, so that it is not needed to click yourself via the Harbor UI. The first robot account has the purpose to be able to scan container images and make necessary needed actions. For this, various reading rights are granted to this robot account. The second robot account has push and pull rights on a project, while the third robot account only has pull rights. The fourth robot account has pull, push and can create labels for 2 projects. Note that this is not a project robot account, but is system-wide, specifically for 2 configured projects.

Usage

You can create a script to run the code inside a container:

#!/bin/sh

# Exit immediately if a simple command exits with a nonzero exit value
set -e

docker build -f docker/dockerfile -t harborrobotaccounts .
docker compose -f docker/docker-compose.yml run --rm main

Considerations

I choose to use passwords in Terraform by initially using a placeholder and replacing it with the value of an environment variable. There are more ways to do this, so feel free to change this logic with something else that works for you.

Since Terraform state may contain sensitive values such as robot secrets and passwords, store state securely (for example in encrypted remote backends) and restrict access appropriately.

GitHub source


메타데이터
post_id
629899a7f793
slug
harbor-robot-accounts-with-terraform-629899a7f793
url
https://medium.com/@frunzasamuel/harbor-robot-accounts-with-terraform-629899a7f793
canonical_url
https://medium.com/@frunzasamuel/harbor-robot-accounts-with-terraform-629899a7f793
author_url
https://medium.com/@frunzasamuel
status
ok
fetched_at
2026-07-10 19:15:58