devops-stack-module-application

A DevOps Stack module to deploy a simple Application in Argo CD.

The module creates an Argo CD AppProject using the name given on declaration and then creates an Argo CD Application using the chart that is inside the path for the Git repository that is declared.

Inside that folder, the module expects an Helm chart with a folder structure similar to the following (as is standard practice):

application_folder
  ├── Chart.yaml
  ├── charts
  │   ├── dependency1.tar.gz
  │   └── dependency2.tar.gz
  ├── secrets.yaml
  ├── templates
  │   ├── template1.yaml
  │   ├── template2.yaml
  │   ├── template3.yaml
  │   └── _helpers.tpl
  └── values.yaml

By default, the created AppProject can only create an Application within a Namespace of the same name or within a Namespace declared on the module declaration. Besides that, the AppProject has the permission to create any kind of Kubernetes resources inside the destination cluster, but you can restrict the allowed resources if you need to.

Usage

This module can be declared by adding the following block on your Terraform configuration:

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

  name             = "application-name"
  argocd_namespace = module.argocd_bootstrap.argocd_namespace

  source_repo            = "https://address.com/owner/repository.git"
  source_repo_path       = "path/to/chart"
  source_target_revision = "branch"

  dependency_ids = {
    argocd = module.argocd.id
  }
}

A more complex declaration, that defines the Namespace and also the AppProject allowed resources, would look like this:

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

  name             = "application-name"
  argocd_namespace = module.argocd_bootstrap.argocd_namespace

  source_repo            = "https://address.com/owner/repository.git"
  source_repo_path       = "path/to/chart"
  source_target_revision = "branch"

  destination_namespace = "namespace"

  project_cluster_resource_whitelist = [
    {
      group = "*"
      kind = "Namespace"
    },
  ]

  project_namespace_resource_whitelist = [
    {
      group = "apps"
      kind = "Deployment"
    },
    {
      group = "*"
      kind = "Service"
    },
  ]

  dependency_ids = {
    argocd = module.argocd.id
  }
}

Furthermore, you can customize the chart’s values.yaml by adding an Helm configuration as an HCL structure:

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

  name             = "application-name"
  argocd_namespace = module.argocd_bootstrap.argocd_namespace

  source_repo            = "https://address.com/owner/repository.git"
  source_repo_path       = "path/to/chart"
  source_target_revision = "branch"

  helm_values = [{ # Note the curly brackets here
    map = {
      string = "string"
      bool   = true
    }
    sequence = [
      {
        key1 = "value1"
        key2 = "value2"
      },
      {
        key1 = "value1"
        key2 = "value2"
      },
    ]
    sequence2 = [
      "string1",
      "string2"
    ]
  }]

  dependency_ids = {
    argocd = module.argocd.id
  }
}

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 like in 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 "module_name" {
  source = "git::https://github.com/camptocamp/devops-stack-module-application.git?ref=<RELEASE>"

  name             = "application-name"
  argocd_namespace = module.argocd_bootstrap.argocd_namespace

  source_repo            = "git@address.com:owner/repository"
  source_repo_path       = "path/to/chart"
  source_target_revision = "branch"

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

  dependency_ids = {
    argocd = module.argocd.id
  }
}

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 like in 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 this use-case, Argo CD only needs read access).
module "module_name" {
  source = "git::https://github.com/camptocamp/devops-stack-module-application.git?ref=<RELEASE>"

  name             = "application-name"
  argocd_namespace = module.argocd_bootstrap.argocd_namespace

  source_repo            = "https://address.com/owner/repository.git"
  source_repo_path       = "path/to/chart"
  source_target_revision = "branch"

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

  dependency_ids = {
    argocd = module.argocd.id
  }
}

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 to the AppProject and Application.

Type: string

source_repo

Description: Repository where to retrieve the application’s chart.

Type: string

source_repo_path

Description: Path for the application’s chart in the source repository.

Type: string

source_target_revision

Description: Git target revision for the application chart.

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.

Type: string

Default: "argocd"

helm_values

Description: Helm values, passed as a list of HCL structures. These values are concatenated with the default ones and then passed to the application’s charts.

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

Type: map(string)

Default: {}

project_dest_cluster_name

Description: Allowed destination cluster name in the AppProject.

Type: string

Default: "in-cluster"

project_dest_cluster_address

Description: Allowed destination cluster address in the AppProject. If you define this variable, any value passed in the project_dest_cluster_name variable is ignored.

Type: string

Default: null

destination_namespace

Description: Namespace where the application will be deployed. By default it is the same as the application’s name defined by var.name. We use a ternary operator to conditionally define the Namespace only if it is defined on the module’s instantiation: namespace = var.destination_namespace == null ? var.name : var.destination_namespace.

Type: string

Default: null

project_cluster_resource_whitelist

Description: Cluster-scoped resources allowed to be deployed in the Argo CD AppProject created by the module. The group must be a Kubernetes API group such as core or apps and the kind must be a Kubernetes Kinds/Object Schemas such as Namespace or ClusterRole (note that only resources like these ones are compatible with this setting, the other resources are only Namespace-scoped). You can see the API Groups here.

Type:

list(object({
    group = string
    kind  = string
  }))

Default:

[
  {
    "group": "*",
    "kind": "*"
  }
]

project_namespace_resource_whitelist

Description: Namespace-scoped resources allowed to be deployed in the Argo CD AppProject created by the module. The group must be a Kubernetes API group such as core or apps and the kind must be a Kubernetes Kinds/Object Schemas such as Pod, ConfigMap, DaemonSet, Deployment, etc. You can see the API Groups here.

Type:

list(object({
    group = string
    kind  = string
  }))

Default:

[
  {
    "group": "*",
    "kind": "*"
  }
]

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

>= 1

= Resources

Name Type

resource

resource

resource

resource

resource

resource

data source

= Inputs

Name Description Type Default Required

Namespace used by Argo CD where the Application and AppProject resources should be created.

string

"argocd"

no

Helm values, passed as a list of HCL structures. These values are concatenated with the default ones and then passed to the application’s charts.

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

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

map(string)

{}

no

Name to give the to the AppProject and Application.

string

n/a

yes

Repository where to retrieve the application’s chart.

string

n/a

yes

Path for the application’s chart in the source repository.

string

n/a

yes

Git target revision for the application chart.

string

n/a

yes

Allowed destination cluster name in the AppProject.

string

"in-cluster"

no

Allowed destination cluster address in the AppProject. If you define this variable, any value passed in the project_dest_cluster_name variable is ignored.

string

null

no

Namespace where the application will be deployed. By default it is the same as the application’s name defined by var.name. We use a ternary operator to conditionally define the Namespace only if it is defined on the module’s instantiation: namespace = var.destination_namespace == null ? var.name : var.destination_namespace.

string

null

no

Cluster-scoped resources allowed to be deployed in the Argo CD AppProject created by the module. The group must be a Kubernetes API group such as core or apps and the kind must be a Kubernetes Kinds/Object Schemas such as Namespace or ClusterRole (note that only resources like these ones are compatible with this setting, the other resources are only Namespace-scoped). You can see the API Groups here.

list(object({
    group = string
    kind  = string
  }))
[
  {
    "group": "*",
    "kind": "*"
  }
]

no

Namespace-scoped resources allowed to be deployed in the Argo CD AppProject created by the module. The group must be a Kubernetes API group such as core or apps and the kind must be a Kubernetes Kinds/Object Schemas such as Pod, ConfigMap, DaemonSet, Deployment, etc. You can see the API Groups here.

list(object({
    group = string
    kind  = string
  }))
[
  {
    "group": "*",
    "kind": "*"
  }
]

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.