devops-stack-module-applicationset

A DevOps Stack module to deploy a generic ApplicationSet in Argo CD.

Usage

You can instantiate this module using the example below:

module "helloworld_apps" {
  source = "git::https://github.com/camptocamp/devops-stack-module-applicationset.git?ref=<RELEASE>"

  depends_on = [module.argocd]

  name                   = "helloworld-apps"
  argocd_namespace       = local.argocd_namespace
  project_dest_namespace = "*"
  project_source_repo    = "https://github.com/camptocamp/devops-stack-helloworld-templates.git"

  generators = [
    {
      git = {
        repoURL  = "https://github.com/camptocamp/devops-stack-helloworld-templates.git"
        revision = "main"

        directories = [
          {
            path = "apps/*"
          }
        ]
      }
    }
  ]
  template = {
    metadata = {
      name = "{{path.basename}}"
    }

    spec = {
      project = "helloworld-apps"

      source = {
        repoURL        = "https://github.com/camptocamp/devops-stack-helloworld-templates.git"
        targetRevision = "main"
        path           = "{{path}}"

        helm = {
          valueFiles = ["values.yaml","secrets.yaml"]

          # The following value defines this global variables that will be available to all apps in apps/*
          # These are needed to generate the ingresses containing the name and base domain of the cluster.
          values = <<-EOT
            cluster:
              name: "${module.eks.cluster_name}"
              domain: "${module.eks.base_domain}"
          EOT
        }
      }

      destination = {
        name      = "in-cluster"
        namespace = "{{path.basename}}"
      }

      syncPolicy = {
        automated = {
          allowEmpty = false
          selfHeal   = true
          prune      = true
        }
        syncOptions = [
          "CreateNamespace=true"
        ]
      }
    }
  }
}

This module first creates an Argo CD AppProject called helloworld-apps that will contain all other resources.

Then, it creates an Application called helloworld-apps that itself only contains an homonymous ApplicationSet, which is created from the chart inside this repository and using the variables given on the instantiation (name, generators, and template).

argocd appset

It is this ApplicationSet that will contain all the applications you have in the repository you defined in repoURL, in this example is helloworld.

argocd app

As you can see, the ApplicationSet template we give here is flexible enough that it can be configured as you like. For example, since we defined the path in the generator as a wildcard apps/*, we can also take this value to then name each application created depending on name of the folder where the chart is located.

Using a private repository

When your charts are stored inside a private repository, Argo CD needs to have the credentials necessary to have at least read access to the contents of the repository.

This module has 2 variables you can use for that, depending on the type of credentials you want to configure, either HTTPS or SSH.

SSH

You’ll need to pass a string containing a private SSH key in the variable source_credentials_ssh_key (you can pass the value however you want, as long as it is a string) and the repository needs to be of the type git@address.com:owner/repository.

Do not hardcode your SSH private key as the example below! Either create the SSH key using Terraform and pass the output directly or use SOPS to store and pass the key as a secret.
module "helloworld_apps" {
  source = "git::https://github.com/camptocamp/devops-stack-module-applicationset.git?ref=<RELEASE>"

  depends_on = [module.argocd]

  name                   = "helloworld-apps"
  argocd_namespace       = local.argocd_namespace
  project_dest_namespace = "*"
  project_source_repo    = "git@github.com:camptocamp/devops-stack-helloworld-templates.git"

  source_credentials_ssh_key = "-----BEGIN OPENSSH PRIVATE KEY-----\nfoo\nbar\n-----END OPENSSH PRIVATE KEY-----"

  generators = [
    {
      git = {
        repoURL  = "git@github.com:camptocamp/devops-stack-helloworld-templates.git"
        revision = "main"

        # ...

      }
    }
  ]
  template = {
    metadata = {
      name = "{{path.basename}}"
    }

    spec = {
      project = "helloworld-apps"

      source = {
        repoURL  = "git@github.com:camptocamp/devops-stack-helloworld-templates.git"

        # ...

      }

      # ...

    }
  }
}

HTTPS

You’ll need to pass the username and password inside the variable source_credentials_https and the repository needs to be of the type https://address.com/owner/repository.

Do not hardcode your password the example below! It is recommended to pass the value as secret, either using SOPS or another provider. Another best practice would be to use a token you created on Github.com (or another provider) that has the least amount of access needed (in these use-case, Argo CD only needs read access).
module "helloworld_apps" {
  source = "git::https://github.com/camptocamp/devops-stack-module-applicationset.git?ref=<RELEASE>"

  depends_on = [module.argocd]

  name                   = "helloworld-apps"
  argocd_namespace       = local.argocd_namespace
  project_dest_namespace = "*"
  project_source_repo    = "https://github.com/camptocamp/devops-stack-helloworld-templates.git"

  source_credentials_https = {
    username = "your_username"
    password = "your_token_password"
    https_insecure = false
  }

  generators = [
    {
      git = {
        repoURL  = "https://github.com/camptocamp/devops-stack-helloworld-templates.git"
        revision = "main"

        # ...

      }
    }
  ]
  template = {
    metadata = {
      name = "{{path.basename}}"
    }

    spec = {
      project = "helloworld-apps"

      source = {
        repoURL  = "https://github.com/camptocamp/devops-stack-helloworld-templates.git"

        # ...

      }

      # ...

    }
  }
}

Technical Reference

Dependencies

module.argocd

As this is an application, it needs to be deployed after the deployment of Argo CD and consequently this module needs to have this explicit dependency.

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:

name

Description: Name to give the AppProject and ApplicationSet (tecnically there is also an Application where the ApplicationSet will reside that will get the same name).

Type: string

generators

Description: ApplicationSet generators.

Type: any

template

Description: ApplicationSet template.

Type: any

Optional Inputs

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

target_revision

Description: Override of target revision of the application chart.

Type: string

Default: "v2.1.1"

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: IDs of the other modules on which this module depends on.

Type: map(string)

Default: {}

project_appset_dest_cluster_name

Description: Allowed destination cluster name in the AppProject. This is the cluster where the ApplicationSets will reside and could be different than the destination cluster of the ApplicationSet template.

Type: string

Default: "in-cluster"

project_appset_dest_cluster_address

Description: Allowed destination cluster address in the AppProject. This is the cluster where the ApplicationSets will reside and could be different than the destination cluster of the ApplicationSet template. If you define this variable, any value passed in the project_appset_dest_cluster_name variable is ignored.

Type: string

Default: null

project_dest_cluster_name

Description: Allowed destination cluster name in the AppProject. Must be the same as the the one configured in the ApplicationSet template.

Type: string

Default: "in-cluster"

project_dest_cluster_address

Description: Allowed destination cluster address in the AppProject. Must be the same as the the one configured in the ApplicationSet template. If you define this variable, any value passed in the project_dest_cluster_name variable is ignored.

Type: string

Default: null

project_dest_namespace

Description: Allowed destination namespace in the AppProject. Must be the same as the the one configured in the ApplicationSet template.

Type: string

Default: "*"

project_source_repo

Description: Repository allowed to be scraped in this AppProject.

Type: string

Default: "*"

source_credentials_https

Description: Credentials to connect to a private repository. Use this variable when connecting through HTTPS. You’ll need to provide the the username and password values. If the TLS certificate for the HTTPS connection is not issued by a qualified CA, you can set https_insecure as true.

Type:

object({
    username       = string
    password       = string
    https_insecure = optional(bool, false)
  })

Default: null

source_credentials_ssh_key

Description: Credentials to connect to a private repository. Use this variable when connecting to a repository through SSH.

Type: string

Default: null

Outputs

The following outputs are exported:

id

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

Reference in table format

Show tables

= Requirements

Name Version

>= 5

>= 3

>= 1

= Providers

Name Version

>= 3

>= 5

= Resources

Name Type

resource

resource

resource

resource

resource

resource

= Inputs

Name Description Type Default Required

Override of target revision of the application chart.

string

"v2.1.1"

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

IDs of the other modules on which this module depends on.

map(string)

{}

no

Name to give the AppProject and ApplicationSet (tecnically there is also an Application where the ApplicationSet will reside that will get the same name).

string

n/a

yes

ApplicationSet generators.

any

n/a

yes

ApplicationSet template.

any

n/a

yes

Allowed destination cluster name in the AppProject. This is the cluster where the ApplicationSets will reside and could be different than the destination cluster of the ApplicationSet template.

string

"in-cluster"

no

Allowed destination cluster address in the AppProject. This is the cluster where the ApplicationSets will reside and could be different than the destination cluster of the ApplicationSet template. If you define this variable, any value passed in the project_appset_dest_cluster_name variable is ignored.

string

null

no

Allowed destination cluster name in the AppProject. Must be the same as the the one configured in the ApplicationSet template.

string

"in-cluster"

no

Allowed destination cluster address in the AppProject. Must be the same as the the one configured in the ApplicationSet template. If you define this variable, any value passed in the project_dest_cluster_name variable is ignored.

string

null

no

Allowed destination namespace in the AppProject. Must be the same as the the one configured in the ApplicationSet template.

string

"*"

no

Repository allowed to be scraped in this AppProject.

string

"*"

no

Credentials to connect to a private repository. Use this variable when connecting through HTTPS. You’ll need to provide the the username and password values. If the TLS certificate for the HTTPS connection is not issued by a qualified CA, you can set https_insecure as true.

object({
    username       = string
    password       = string
    https_insecure = optional(bool, false)
  })

null

no

Credentials to connect to a private repository. Use this variable when connecting to a repository through SSH.

string

null

no

= Outputs

Name Description

id

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