Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- New variable `operate_at_root_group_level` to simplify configuration and replace the combination of `gitlab_agent_grant_access_to_entire_root_namespace` and `gitlab_agent_create_variables_in_root_namespace`
- New variable `groups_enabled` to specify groups where the GitLab Agent should be enabled (when not operating at root group level)
- New variable `projects_enabled` to specify projects where the GitLab Agent should be enabled (when not operating at root group level)
- Auto-detection of parent group when `operate_at_root_group_level = false` and no groups/projects are specified
- Support for creating CI/CD variables in multiple groups and projects simultaneously
- Dynamic generation of agent configuration file based on enabled groups/projects using `yamlencode()`
- New outputs: `gitlab_enabled_groups`, `gitlab_enabled_projects`, `gitlab_parent_group_auto_detected`, `operate_at_root_group_level`

### Changed

- Agent configuration file is now dynamically generated based on `operate_at_root_group_level` and enabled groups/projects
- CI/CD variables can now be created in multiple targets (root group, specific groups, or specific projects) depending on configuration
- Output `gitlab_root_namespace_id` now returns `null` when not operating at root group level

### Deprecated

- Variable `gitlab_agent_grant_access_to_entire_root_namespace` - use `operate_at_root_group_level` instead
- Variable `gitlab_agent_create_variables_in_root_namespace` - behavior is now determined by `operate_at_root_group_level`

## [0.12.0] - 2025-05-19

[Compare with previous version](https://github.com/sparkfabrik/terraform-gitlab-kubernetes-gitlab-agent/compare/0.11.0...0.12.0)
Expand Down
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,60 @@ This module creates all the necessary resources to deploy a Gitlab Agent on a Ku

It uses the Gitlab provider to register the agent on the Gitlab server. The generated registration token is use to create an Helm release of the Gitlab Agent in the cluster.

If required (`gitlab_agent_grant_access_to_entire_root_namespace` configured to `true`), it also creates the necessary configuration files in the given Gitlab project, granting access to all the projects in the root namespace and subgroups.
The module supports multiple configuration modes:

- **Root Group Level** (default): The agent has access to the entire root namespace and CI/CD variables are created in the root group
- **Auto-detect Parent**: When not operating at root level and no specific groups/projects are provided, the module automatically detects the parent group of the agent project
- **Specific Groups/Projects**: Enable the agent only for specific groups or projects, with variables created in those locations

**ATTENTION**: you have to manually create the project that will host the Gitlab Agent configuration in Gitlab before running this module.

From version `0.7.0`, if you set `gitlab_project_name` the module will create Gitlab project automatically. This new behavior requires the provider to have the proper permissions to create the project in the namespace.

## Configuration Examples

### Example 1: Root Group (Default)
```hcl
module "gitlab_agent" {
source = "github.com/sparkfabrik/terraform-gitlab-kubernetes-gitlab-agent"

gitlab_project_path_with_namespace = "my-org/agents-project"
gitlab_agent_name = "production-agent"
namespace = "gitlab-agent"
}
```

### Example 2: Auto-detect Parent Group
```hcl
module "gitlab_agent" {
source = "github.com/sparkfabrik/terraform-gitlab-kubernetes-gitlab-agent"

gitlab_project_path_with_namespace = "my-org/team-a/subgroup/agents"
gitlab_agent_name = "team-agent"
namespace = "gitlab-agent"

operate_at_root_group_level = false
# Parent group "my-org/team-a/subgroup" will be automatically detected
}
```

### Example 3: Specific Groups
```hcl
module "gitlab_agent" {
source = "github.com/sparkfabrik/terraform-gitlab-kubernetes-gitlab-agent"

gitlab_project_path_with_namespace = "my-org/infrastructure/agents"
gitlab_agent_name = "shared-agent"
namespace = "gitlab-agent"

operate_at_root_group_level = false
groups_enabled = [
"my-org/team-a",
"my-org/team-b"
]
}
```

## RBAC configuration for the Gitlab Agent service account

This module uses the default configuration of the Gitlab Agent Helm chart. The default configuration grants to the Gitlab Agent service account the `cluster-admin` ClusterRole. If you want to change this configuration, you can use the `helm_additional_values` variable to pass additional values to the Helm chart.
Expand Down
102 changes: 99 additions & 3 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,47 @@ locals {
gitlab_agent_commmit_message_computed = replace(var.gitlab_agent_commmit_message, "{{gitlab_agent_name}}", var.gitlab_agent_name)
k8s_gitlab_agent_token_secret_name_computed = replace(var.k8s_gitlab_agent_token_secret_name, "{{gitlab_agent_name}}", var.gitlab_agent_name)

# Backward compatibility: map old variables to new operate_at_root_group_level
operate_at_root_group_level_computed = var.operate_at_root_group_level != null ? var.operate_at_root_group_level : (
var.gitlab_agent_grant_user_access_to_root_namespace != null ? var.gitlab_agent_grant_user_access_to_root_namespace : true
)

# Determine the parent group of the project
project_path_parts = split("/", var.gitlab_project_path_with_namespace)
parent_group_path = length(local.project_path_parts) > 1 ? join("/", slice(local.project_path_parts, 0, length(local.project_path_parts) - 1)) : local.project_root_namespace

# Determine if we are in auto-parent mode
auto_detect_parent = !local.operate_at_root_group_level_computed && length(concat(var.groups_enabled, var.projects_enabled)) == 0

# Final list of groups to enable
groups_to_enable = local.operate_at_root_group_level_computed ? [local.project_root_namespace] : (
local.auto_detect_parent ? [local.parent_group_path] : var.groups_enabled
)

# Final list of projects to enable
projects_to_enable = local.operate_at_root_group_level_computed ? [] : (
local.auto_detect_parent ? [] : var.projects_enabled
)

# Gitlab Agent configuration file
final_configuration_file_content = var.gitlab_agent_custom_config_file_content != "" ? var.gitlab_agent_custom_config_file_content : (var.gitlab_agent_grant_access_to_entire_root_namespace ? templatefile("${path.module}/files/config.yaml.tftpl", { root_namespace = data.gitlab_group.root_namespace.path, gitlab_agent_append_to_config_file = var.gitlab_agent_append_to_config_file, gitlab_agent_grant_user_access_to_root_namespace = var.gitlab_agent_grant_user_access_to_root_namespace }) : "")
final_configuration_file_content = var.gitlab_agent_custom_config_file_content != "" ? var.gitlab_agent_custom_config_file_content : (
local.operate_at_root_group_level_computed ? templatefile("${path.module}/files/config.yaml.tftpl", {
root_namespace = data.gitlab_group.root_namespace.path,
gitlab_agent_append_to_config_file = var.gitlab_agent_append_to_config_file,
gitlab_agent_grant_user_access_to_root_namespace = var.gitlab_agent_grant_user_access_to_root_namespace
}) : (
length(local.groups_to_enable) > 0 || length(local.projects_to_enable) > 0 ? yamlencode({
ci_access = merge(
length(local.groups_to_enable) > 0 ? {
groups = [for g in local.groups_to_enable : { id = g }]
} : {},
length(local.projects_to_enable) > 0 ? {
projects = [for p in local.projects_to_enable : { id = p }]
} : {}
)
}) : ""
)
)

# Gitlab Agent CI/CD variables
gitlab_agent_kubernetes_context_variables = {
Expand All @@ -41,6 +80,24 @@ data "gitlab_group" "root_namespace" {
full_path = local.project_root_namespace
}

# Parent group data source parent group if auto-detect
data "gitlab_group" "parent_group" {
count = local.auto_detect_parent ? 1 : 0
full_path = local.parent_group_path
}

# Data source for the specified groups
data "gitlab_group" "enabled_groups" {
for_each = !local.operate_at_root_group_level_computed && !local.auto_detect_parent ? toset(var.groups_enabled) : toset([])
full_path = each.value
}

# Data source for the specified projects
data "gitlab_project" "enabled_projects" {
for_each = !local.operate_at_root_group_level_computed && !local.auto_detect_parent ? toset(var.projects_enabled) : toset([])
path_with_namespace = each.value
}

resource "gitlab_project" "project" {
count = local.use_existing_project == 0 ? 1 : 0
name = var.gitlab_project_name
Expand Down Expand Up @@ -78,8 +135,9 @@ resource "gitlab_repository_file" "this" {
]
}

resource "gitlab_group_variable" "this" {
for_each = var.gitlab_agent_create_variables_in_root_namespace ? local.gitlab_agent_kubernetes_context_variables : {}
# Variabili per root group (quando operate_at_root_group_level è true)
resource "gitlab_group_variable" "root_namespace" {
for_each = local.operate_at_root_group_level_computed ? local.gitlab_agent_kubernetes_context_variables : {}

group = data.gitlab_group.root_namespace.group_id
key = each.key
Expand All @@ -94,6 +152,44 @@ resource "gitlab_group_variable" "this" {
]
}

# Variabili per gruppi specifici (quando operate_at_root_group_level è false)
resource "gitlab_group_variable" "enabled_groups" {
for_each = !local.operate_at_root_group_level_computed && length(local.groups_to_enable) > 0 ? {
for pair in setproduct(keys(local.gitlab_agent_kubernetes_context_variables), local.groups_to_enable) :
"${pair[1]}_${pair[0]}" => {
group_path = pair[1]
key = pair[0]
value = local.gitlab_agent_kubernetes_context_variables[pair[0]]
}
} : {}

group = local.auto_detect_parent && each.value.group_path == local.parent_group_path ? data.gitlab_group.parent_group[0].group_id : data.gitlab_group.enabled_groups[each.value.group_path].group_id
key = each.value.key
value = each.value.value
protected = false
masked = false

}

# Variabili per progetti specifici (quando operate_at_root_group_level è false)
resource "gitlab_project_variable" "enabled_projects" {
for_each = !local.operate_at_root_group_level_computed && length(local.projects_to_enable) > 0 ? {
for pair in setproduct(keys(local.gitlab_agent_kubernetes_context_variables), local.projects_to_enable) :
"${pair[1]}_${pair[0]}" => {
project_path = pair[1]
key = pair[0]
value = local.gitlab_agent_kubernetes_context_variables[pair[0]]
}
} : {}

project = data.gitlab_project.enabled_projects[each.value.project_path].id
key = each.value.key
value = each.value.value
protected = false
masked = false

}

# Kubernetes resources
resource "kubernetes_namespace_v1" "this" {
count = var.create_namespace ? 1 : 0
Expand Down
24 changes: 22 additions & 2 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ output "gitlab_agents_project_id" {
}

output "gitlab_root_namespace_id" {
description = "The ID of the root namespace of the Gitlab Agents project."
value = data.gitlab_group.root_namespace.group_id
description = "The ID of the root namespace of the Gitlab Agents project. Only available when operate_at_root_group_level is true."
value = local.operate_at_root_group_level_computed ? data.gitlab_group.root_namespace.group_id : null
}

output "gitlab_enabled_groups" {
description = "List of groups where the GitLab Agent has been enabled with variables."
value = local.groups_to_enable
}

output "gitlab_enabled_projects" {
description = "List of projects where the GitLab Agent has been enabled with variables."
value = local.projects_to_enable
}

output "gitlab_parent_group_auto_detected" {
description = "Whether the parent group was automatically detected."
value = local.auto_detect_parent
}

output "operate_at_root_group_level" {
description = "The computed value of operate_at_root_group_level (includes backward compatibility)."
value = local.operate_at_root_group_level_computed
}
24 changes: 15 additions & 9 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,26 @@ variable "gitlab_agent_token_description" {
default = "Token for the Gitlab Agent {{gitlab_agent_name}}."
}

variable "gitlab_agent_grant_access_to_entire_root_namespace" {
description = "Grant access to the entire root namespace. If false, you can provide a custom configuration file content using the variable `gitlab_agent_custom_config_file_content`. Otherwise, you will have to manually manage the access to the Gitlab Agent committing the proper configuration to the Gitlab project."
variable "operate_at_root_group_level" {
description = "Operate at root group level. If true, grants access to entire root namespace and creates variables in root group. If false, behavior depends on groups_enabled and projects_enabled. This replaces gitlab_agent_grant_access_to_entire_root_namespace and gitlab_agent_create_variables_in_root_namespace."
type = bool
default = true
}

variable "groups_enabled" {
description = "List of group paths where the GitLab Agent should be enabled. Only used when operate_at_root_group_level is false. If empty and projects_enabled is also empty, the parent group of the agent project will be used automatically."
type = list(string)
default = []
}

variable "projects_enabled" {
description = "List of project paths (with namespace) where the GitLab Agent should be enabled. Only used when operate_at_root_group_level is false. If empty and groups_enabled is also empty, the parent group of the agent project will be used automatically."
type = list(string)
default = []
}

variable "gitlab_agent_grant_user_access_to_root_namespace" {
description = "Grant `user_access` to the root namespace."
description = "DEPRECATED: Use operate_at_root_group_level instead.Grant `user_access` to the root namespace."
type = bool
default = false
}
Expand Down Expand Up @@ -68,12 +80,6 @@ variable "gitlab_agent_branch_name" {
default = "main"
}

variable "gitlab_agent_create_variables_in_root_namespace" {
description = "Create two Gitlab CI/CD variables in the root namespace useful to configure the Kubernetes context and use the Gitlab Agent. These variables are created in the root namespace of the project defined in `gitlab_project_path_with_namespace`, which is the project that hosts the Gitlab Agent configuration."
type = bool
default = true
}

variable "gitlab_agent_variable_name_agent_id" {
description = "The name of the Gitlab CI/CD variable that stores the Gitlab Agent ID."
type = string
Expand Down