devops-stack-module-argocd

A DevOps Stack module to deploy and configure Argo CD.

The Argo CD chart used by this module is shipped in this repository as well, in order to avoid any unwanted behaviors caused by unsupported versions.

Current Chart Version Original Repository Default Values

5.27.1

Chart

values.yaml

Usage

The root of this repository contains the final Argo CD module to be deployed, which uses a [Terraform provider for Argo CD] to deploy the Argo CD chart. On the first deployment of a cluster, you’ll want to use the bootstrap module instead. Check the bootstrap module’s documentation for more information.

To deploy the final Argo CD module, you’ll need to add the following declaration on your Terraform configuration:

module "argocd" {
  source = "git::https://github.com/camptocamp/devops-stack-module-argocd.git?ref=v1.1.0"

  cluster_name   = local.cluster_name
  base_domain    = local.base_domain
  cluster_issuer = local.cluster_issuer

  admin_enabled            = "true"
  namespace                = module.argocd_bootstrap.argocd_namespace
  accounts_pipeline_tokens = module.argocd_bootstrap.argocd_accounts_pipeline_tokens
  server_secretkey         = module.argocd_bootstrap.argocd_server_secretkey

  dependency_ids = {
    argocd                = module.argocd_bootstrap.id
    traefik               = module.traefik.id
    cert-manager          = module.cert-manager.id
    oidc                  = module.oidc.id
    kube-prometheus-stack = module.kube-prometheus-stack.id
  }
}

A more complex declaration, that includes a OIDC configuration for the login (this way you avoid having to login using the admin password found on a Kubernetes secret) and the configuration of some other source repositories, would be:

module "argocd" {
  source = "git::https://github.com/camptocamp/devops-stack-module-argocd.git?ref=v1.1.0"

  ...

  oidc = {
    name         = "OIDC"
    issuer       = module.oidc.oidc.issuer_url
    clientID     = module.oidc.oidc.client_id
    clientSecret = module.oidc.oidc.client_secret
    requestedIDTokenClaims = {
      groups = {
        essential = true
      }
    }
    requestedScopes = [
      "openid", "profile", "email"
    ]
  }

  repositories = {
    cluster-apps = {
      ...
    }
    user-apps = {
      ...
    }
  }

  ...
}
The sources can also be configured using the Application and ApplicationSet modules.

You can also overload the policy.csv as shown in the following example:

module "argocd" {
  source = "git::https://github.com/camptocamp/devops-stack-module-argocd.git?ref=v1.1.0"

  ...

  helm_values = [{
    argo-cd = {
      configs = {
        rbac = {
          "policy.csv" = <<-EOT
          g, pipeline, role:admin
          g, argocd-admin, role:admin
          p, role:read-sync, applications, get, */*, allow
          p, role:read-sync, applications, sync, */*, allow
          p, role:read-sync, repositories, get, *, allow
          p, role:read-sync, projects, get, *, allow
          EOT
        }
      }
    }
  }]

  ...
}
By default, this module has a policy.csv that is configured to give administrator access to any user belonging to the groups argocd-admin or devops-stack-admins.

Custom Plugins

This module installs two custom plugins:

  1. "kustomized-helm" is just a combination of Kustomize and Helm that allows using Helm charts and then applying Kustomize overrides to the result.

  2. "helmfile-sops" adds support for Helmfile managed applications, and integrates the support for SOPS. This is a custom plugin developed by Camptocamp (source code available here).

When using SOPS, you will want to give it access to an external secrets management or encryption service. For this, you will want to pass an AWS IAM role, and Azure Workload Identity Client-ID, or an Azure AAD Pod Identity using the variables repo_server_iam_role_arn, repo_server_azure_workload_identity_clientid`, or repo_server_aadpodidbinding.

Troubleshooting

When deploying this module of Argo CD you may experience connection errors (which is normal given that argocd-server pod could have been redeployed).

│ Error: Error while waiting for application argocd to be created
│
│   with module.argocd.argocd_application.this,
│   on .terraform/modules/argocd/main.tf line 55, in resource "argocd_application" "this":
│   55: resource "argocd_application" "this" {
│
│ error while waiting for application argocd to be synced and healthy: rpc error: code = Unavailable desc = connection error: desc = "transport: error while dialing: dial tcp 127.0.0.1:44461: connect:
│ connection refused"

When bootstrapping a cluster for the first time, you can simply run terraform apply again and the deployment should finish correctly.

However, on some cases (notably when upgrading the Argo CD module), this error could leave the Terraform resource tainted.

terraform plan
  # module.devops_stack_blue.module.argocd.argocd_application.this is tainted, so must be replaced
-/+ resource "argocd_application" "this" {
      ~ id      = "argocd:argocd" -> (known after apply)
        # (2 unchanged attributes hidden)

      ~ metadata {
        ...
        }

      ~ spec {
        ...
        }
    }

Plan: 1 to add, 1 to change, 1 to destroy.

Untainting the resource with the command terraform untaint module.argocd.argocd_application.this should solve the issue.

Technical Documentation

Dependencies

module.argocd_bootstrap.id

Obviously, this module needs an already working Argo CD (the bootstrap), so it depends on module.argocd_bootstrap.

module.traefik.id

Since there is an ingress deployed with this module, it needs to be deployed after Traefik so it depends on module.ingress.

module.traefik.id

For the same reason as the previous dependency, it needs to be deployed after cert-manager so it depends on module.cert-manager.

module.oidc.id

Only for the platforms that deploy a cluster module, such as EKS or KinD, there is also a the dependency on module.oidc.id.

module.kube-prometheus-stack.id

Finally, the kube-prometheus-stack is a requirement because this Argo CD module requires the ServiceMonitor CRD so it depends on module.kube-prometheus-stack.

Requirements

The following requirements are needed by this module:

Providers

The following providers are used by this module:

Resources

The following resources are used by this module:

Required Inputs

The following input variables are required:

cluster_name

Description: Name given to the cluster. Value used for the ingress' URL of the application.

Type: string

base_domain

Description: Base domain of the cluster. Value used for the ingress' URL of the application.

Type: string

accounts_pipeline_tokens

Description: API token for pipeline account.

Type: string

server_secretkey

Description: Signature key for session validation. Must reuse the bootstrap output containing the secretkey.

Type: string

Optional Inputs

The following input variables are optional (have default values):

argocd_namespace

Description: Namespace used by Argo CD where the Application and AppProject resources should be created. Normally, it should take the outputof the namespace from the bootstrap module.

Type: string

Default: "argocd"

target_revision

Description: Override of target revision of the application chart.

Type: string

Default: "v3.1.0"

cluster_issuer

Description: SSL certificate issuer to use. Usually you would configure this value as letsencrypt-staging or letsencrypt-prod on your root *.tf files.

Type: string

Default: "ca-issuer"

namespace

Description: Namespace where to deploy Argo CD.

Type: string

Default: "argocd"

helm_values

Description: Helm chart value overrides. They should be passed as a list of HCL structures.

Type: any

Default: []

app_autosync

Description: Automated sync options for the Argo CD Application resource.

Type:

object({
    allow_empty = optional(bool)
    prune       = optional(bool)
    self_heal   = optional(bool)
  })

Default:

{
  "allow_empty": false,
  "prune": true,
  "self_heal": true
}

dependency_ids

Description: n/a

Type: map(string)

Default: {}

oidc

Description: OIDC settings for the log in to the Argo CD web interface.

Type: any

Default: null

rbac

Description: RBAC settings for the Argo CD users.

Type:

object({
    scopes         = optional(string, "[groups, cognito:groups, roles]")
    policy_default = optional(string, "")
    policy_csv = optional(string, <<-EOT
                                    g, pipeline, role:admin
                                    g, argocd-admin, role:admin
                                    g, devops-stack-admins, role:admin
                                  EOT
    )
  })

Default: {}

repositories

Description: List of repositories to add to Argo CD.

Type: map(map(string))

Default: {}

ssh_known_hosts

Description: List of SSH known hosts to add to Argo CD. Check the official values.yaml to get the format to pass this value.

Type: string

Default: ""

exec_enabled

Description: Flag to enable the web-based terminal on Argo CD. Do not forget to set the appropriate RBAC configuration to your users/groups.

Type: bool

Default: false

admin_enabled

Description: Flag to indicate whether to enable the administrator user.

Type: bool

Default: false

extra_accounts

Description: List of accounts for which tokens will be generated.

Type: list(string)

Default: []

repo_server_iam_role_arn

Description: IAM role ARN to associate with the argocd-repo-server ServiceAccount. This role can be used to give SOPS access to AWS KMS.

Type: string

Default: null

repo_server_azure_workload_identity_clientid

Description: Azure AD Workload Identity Client-ID to associate with argocd-repo-server. This role can be used to give SOPS access to a Key Vault.

Type: string

Default: null

repo_server_aadpodidbinding

Description: Azure AAD Pod Identity to associate with the argocd-repo-server Pod. This role can be used to give SOPS access to a Key Vault.

Type: string

Default: null

helmfile_cmp_version

Description: Version of the helmfile-cmp plugin.

Type: string

Default: "0.1.1"

helmfile_cmp_env_variables

Description: List of environment variables to attach to the helmfile-cmp plugin, usually used to pass authentication credentials. Use a an explicit format or take the values from a Kubernetes secret.

Type:

list(object({
    name  = optional(string)
    value = optional(string)
    valueFrom = optional(object({
      secretKeyRef = optional(object({
        name = optional(string)
        key  = optional(string)
      }))
    }))
  }))

Default: []

Outputs

The following outputs are exported:

id

Description: ID to pass other modules in order to refer to this module as a dependency.

extra_tokens

Description: Map of extra accounts that were created and their tokens.

Reference in table format

Show tables

= Requirements

Name Version

>= 1.2

>= 5

>= 1

jwt

>= 1.1

>= 3

>= 3

>= 0.9

>= 1.6

= Providers

Name Version

>= 3

jwt

>= 1.1

>= 0.9

>= 3

>= 1.6

>= 5

= Resources

Name Type

resource

resource

resource

resource

resource

resource

resource

data source

= Inputs

Name Description Type Default Required

Name given to the cluster. Value used for the ingress' URL of the application.

string

n/a

yes

Base domain of the cluster. Value used for the ingress' URL of the application.

string

n/a

yes

Namespace used by Argo CD where the Application and AppProject resources should be created. Normally, it should take the outputof the namespace from the bootstrap module.

string

"argocd"

no

Override of target revision of the application chart.

string

"v3.1.0"

no

SSL certificate issuer to use. Usually you would configure this value as letsencrypt-staging or letsencrypt-prod on your root *.tf files.

string

"ca-issuer"

no

Namespace where to deploy Argo CD.

string

"argocd"

no

Helm chart value overrides. They should be passed as a list of HCL structures.

any

[]

no

Automated sync options for the Argo CD Application resource.

object({
    allow_empty = optional(bool)
    prune       = optional(bool)
    self_heal   = optional(bool)
  })
{
  "allow_empty": false,
  "prune": true,
  "self_heal": true
}

no

n/a

map(string)

{}

no

OIDC settings for the log in to the Argo CD web interface.

any

null

no

RBAC settings for the Argo CD users.

object({
    scopes         = optional(string, "[groups, cognito:groups, roles]")
    policy_default = optional(string, "")
    policy_csv = optional(string, <<-EOT
                                    g, pipeline, role:admin
                                    g, argocd-admin, role:admin
                                    g, devops-stack-admins, role:admin
                                  EOT
    )
  })

{}

no

List of repositories to add to Argo CD.

map(map(string))

{}

no

List of SSH known hosts to add to Argo CD. Check the official values.yaml to get the format to pass this value.

string

""

no

Flag to enable the web-based terminal on Argo CD. Do not forget to set the appropriate RBAC configuration to your users/groups.

bool

false

no

Flag to indicate whether to enable the administrator user.

bool

false

no

API token for pipeline account.

string

n/a

yes

Signature key for session validation. Must reuse the bootstrap output containing the secretkey.

string

n/a

yes

List of accounts for which tokens will be generated.

list(string)

[]

no

IAM role ARN to associate with the argocd-repo-server ServiceAccount. This role can be used to give SOPS access to AWS KMS.

string

null

no

Azure AD Workload Identity Client-ID to associate with argocd-repo-server. This role can be used to give SOPS access to a Key Vault.

string

null

no

Azure AAD Pod Identity to associate with the argocd-repo-server Pod. This role can be used to give SOPS access to a Key Vault.

string

null

no

Version of the helmfile-cmp plugin.

string

"0.1.1"

no

List of environment variables to attach to the helmfile-cmp plugin, usually used to pass authentication credentials. Use a an explicit format or take the values from a Kubernetes secret.

list(object({
    name  = optional(string)
    value = optional(string)
    valueFrom = optional(object({
      secretKeyRef = optional(object({
        name = optional(string)
        key  = optional(string)
      }))
    }))
  }))

[]

no

= Outputs

Name Description

id

ID to pass other modules in order to refer to this module as a dependency.

Map of extra accounts that were created and their tokens.