Using ArgoCD Image Updater with GCP Artifact Registry
Published: 2024-03-25 07:23:03

We may want the updater to trigger a deployment when an image is pushed to Artifact Registry. 

We install the updater in our cluster using terraform. 

```
resource "helm_release" "image_updater" {
  name       = "argocd-image-updater"
  repository = "https://argoproj.github.io/argo-helm"
  chart      = "argocd-image-updater"
  namespace  = "argocd"
  version    = "0.9.6"

  values = [
    <<EOF
config:
  registries:
    - name: GCP Artifact Registry
      api_url: https://europe-west1-docker.pkg.dev
      prefix: europe-west1-docker.pkg.dev
      credentials: ext:/auth/auth.sh
      credsexpire: 30m
volumes:
- configMap:
    defaultMode: 0755
    name: auth-cm
  name: auth
volumeMounts:
- mountPath: /auth
  name: auth
EOF
  ]
}
```

We authenticate to Artifact Registry using the script auth.sh. The process to do this is outlined [here](https://github.com/argoproj-labs/argocd-image-updater/issues/319).

To enable argocd authenticate to Artifact Registry, we make use of GCP workload indentities. This is equivalent to IRSA (IAM Role for Service Accounts) in AWS. We set up a kubernetes service account which is annotated with a GCP service account.

```
resource "kubernetes_service_account" "ksa" {
  metadata {
    name      = "argocd-image-updater-sa"
    namespace = "argocd"
    annotations = {
      "iam.gke.io/gcp-service-account" = google_service_account.app_sa.email
    }
  }
}

resource "google_service_account" "app_sa" {
  account_id   = "argocd-image-updater-sa"
  display_name = "argocd-image-updater-sa"
}
```

The GCP SA needs artifactregistry permissions.

```
resource "google_project_iam_member" "artifactregistry_repoAdmin" {
  project = var.project_id
  role    = "roles/artifactregistry.repoAdmin"
  member  = "serviceAccount:${google_service_account.app_sa.email}"
}
```

Allow the Kubernetes service account to impersonate the GCP service account by adding an IAM policy binding between the two service accounts. This binding allows the Kubernetes service account to act as the GCP service account.

`gcloud iam service-accounts add-iam-policy-binding GSA_NAME@GSA_PROJECT.iam.gserviceaccount.com \
 --role roles/iam.workloadIdentityUser \
 --member "serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]"`

```
resource "google_service_account_iam_binding" "argo_workload_identity_binding" {
  service_account_id = google_service_account.app_sa.name
  role               = "roles/iam.workloadIdentityUser"
  members            = ["serviceAccount:${var.project_id}.svc.id.goog[argocd/argocd-image-updater-sa]"]
}
```

Set up a config map contaning the artifact registry authentication script. This config map is mounted to the argocd image updater deployment as a volume. 

```
resource "kubernetes_config_map" "auth_cm" {
  metadata {
    name      = "auth-cm"
    namespace = "argocd"
  }

  data = {
    "auth.sh" = <<EOF
#!/bin/sh
ACCESS_TOKEN=$(wget --header 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token -q -O - | grep -Eo '"access_token":.*?[^\\]",' | cut -d '"' -f 4)
echo "oauth2accesstoken:$ACCESS_TOKEN"
EOF
  }
}
```

We add these annotations to our ArgoCD application. 



```
  annotations:
    argocd-image-updater.argoproj.io/image-list: repo=repo=europe-west1-docker.pkg.dev/tumahela/demo-app/demo-ap
    argocd-image-updater.argoproj.io/repo.update-strategy: latest
```

To troubleshooot, we can log in to the argocd container.

When we push an image, after a short while argocd will trigger a deployment with this new image.

`kubectl exec -n argocd -it argocd-image-updater-7fc87697d5-qtnhn -- ash`

Test the connection to ECR.

`argocd-image-updater test europe-west1-docker.pkg.dev/tumahela/demo-app/demo-app --credentials ext:/auth/auth.sh --update-strategy latest`

Trigger a deploymemnt.

`argocd-image-updater run --once --loglevel trace --argocd-namespace argocd  --metrics-port 0`