Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
55 changes: 16 additions & 39 deletions modules/aks/github-connector/backplane/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,59 +9,36 @@
# - update image pull secret
# - deployment
#
data "azurerm_subscription" "current" {
locals {
acr_resource_group_name = coalesce(var.acr.resource_group_name, azurerm_resource_group.bb_github_connector.name)
}

data "azurerm_kubernetes_cluster" "aks" {
name = "aks"
resource_group_name = "aks-rg"
name = var.aks.cluster_name
resource_group_name = var.aks.resource_group_name
}

resource "azurerm_resource_group" "bb_github_connector" {
name = "bb-github-connector"
location = "Germany West Central"
}

# SPN for Terraform state

resource "azuread_application" "bb_github_connector" {
display_name = "bb-github-connector"
}

resource "azuread_service_principal" "bb_github_connector" {
client_id = azuread_application.bb_github_connector.client_id
}

resource "azuread_service_principal_password" "bb_github_connector" {
service_principal_id = azuread_service_principal.bb_github_connector.id
}

resource "azurerm_role_assignment" "bb_github_connector" {
scope = data.azurerm_subscription.current.id
role_definition_name = "Contributor" # TODO: restrict permissions
principal_id = azuread_service_principal.bb_github_connector.object_id
}

resource "azurerm_role_assignment" "terraform_state" {
role_definition_name = "Storage Blob Data Owner"
principal_id = azuread_service_principal.bb_github_connector.object_id
scope = var.tfstates_resource_manager_id
name = var.resource_prefix
location = var.acr.location
}

# Container registry
# We're using a shared container registry for all consumers of this building block.
# This could easily be changed to deploy a dedicated container registry per building block
# or by making the container registry configurable.
# A shared ACR is used for all building block consumers by default.
# Set var.acr.resource_group_name to place the ACR in an existing resource group.

resource "azurerm_container_registry" "acr" {
name = "githubconnector"
resource_group_name = azurerm_resource_group.bb_github_connector.name
location = azurerm_resource_group.bb_github_connector.location
name = replace(var.resource_prefix, "-", "")
resource_group_name = local.acr_resource_group_name
location = var.acr.location
sku = "Basic"
}

# Service principal used by GitHub Actions to push images to ACR.
# Granted AcrPush (not Contributor) — scoped to this registry only.

resource "azuread_application" "bb_github_connector_acr" {
display_name = "bb-github-connector-acr"
display_name = "${var.resource_prefix}-acr"
}

resource "azuread_service_principal" "bb_github_connector_acr" {
Expand All @@ -74,7 +51,7 @@ resource "azuread_service_principal_password" "bb_github_connector_acr" {

resource "azurerm_role_assignment" "bb_github_connector_acr" {
scope = azurerm_container_registry.acr.id
role_definition_name = "Contributor"
role_definition_name = "AcrPush"
principal_id = azuread_service_principal.bb_github_connector_acr.object_id
}

Expand Down
5 changes: 2 additions & 3 deletions modules/aks/github-connector/backplane/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ output "config_tf" {
description = "Generates a config.tf that can be dropped into meshStack's BuildingBlockDefinition as an encrypted file input to configure this building block."
sensitive = true
value = <<-EOF
provider "kubernetes" {
provider "kubernetes" {
host = "${data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].host}"
cluster_ca_certificate = base64decode("${data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].cluster_ca_certificate}")
client_certificate = base64decode("${data.azurerm_kubernetes_cluster.aks.kube_admin_config[0].client_certificate}")
Expand Down Expand Up @@ -32,6 +32,5 @@ output "config_tf" {
password = "${azuread_service_principal_password.bb_github_connector_acr.value}"
}
}
EOF
EOF
}

34 changes: 20 additions & 14 deletions modules/aks/github-connector/backplane/variables.tf
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
variable "tfstates_resource_manager_id" {
type = string
nullable = false
variable "resource_prefix" {
type = string
default = "bb-github-connector"
description = "Prefix used for all named resources created by this backplane (resource group, app registrations, ACR)."
}

variable "tfstates_resource_group_name" {
type = string
nullable = false
variable "aks" {
type = object({
cluster_name = string
resource_group_name = string
})
description = "Reference to the existing AKS cluster this building block connects to."
}

variable "tfstates_storage_account_name" {
type = string
nullable = false
}

variable "tfstates_storage_container_name" {
type = string
nullable = false
variable "acr" {
type = object({
location = string
resource_group_name = optional(string)
})
description = "Configuration for the shared Azure Container Registry. resource_group_name defaults to the resource group created by this backplane when omitted."
default = {
location = "Germany West Central"
resource_group_name = null
}
}

238 changes: 238 additions & 0 deletions modules/aks/github-connector/meshstack_integration.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
terraform {
required_providers {
meshstack = {
source = "meshcloud/meshstack"
version = "~> 0.19.3"
}
}
}

variable "meshstack" {
type = object({
owning_workspace_identifier = string
})
}

variable "github" {
type = object({
repo_definition_uuid = string
org = string
app_id = string
app_installation_id = string
app_pem_file = string
})
description = "GitHub integration configuration. repo_definition_uuid is the UUID of the deployed GitHub repo building block definition. The app_* fields are the GitHub App credentials for the GitHub Terraform provider."
}

variable "aks" {
type = object({
connector_config_tf_base64 = string
})
description = "AKS integration configuration. connector_config_tf_base64 is the base64-encoded config.tf providing a kubeconfig stub (cluster server endpoint and CA certificate) and ACR credentials to the building block run. It does not contain user credentials — those are provisioned by the building block itself."
}

locals {
config_tf_secret_value = "data:application/octet-stream;base64,${var.aks.connector_config_tf_base64}"
}

variable "tags" {
type = map(list(string))
default = {}
}

variable "notification_subscribers" {
type = list(string)
default = []
}

variable "hub" {
type = object({
git_ref = string
})
default = {
git_ref = "main"
}
description = "Hub release reference. Set git_ref to a tag (e.g. 'v1.2.3') or branch for the meshstack-hub repo."
}

resource "meshstack_building_block_definition" "aks_github_connector" {
metadata = {
owned_by_workspace = var.meshstack.owning_workspace_identifier
tags = var.tags
}

spec = {
description = "CI/CD pipeline using GitHub Actions for secure, scalable AKS deployment. Sets up service accounts, secrets, and workflows for seamless GitHub Actions integration with an AKS namespace."
display_name = "GitHub Actions AKS Connector"
notification_subscribers = var.notification_subscribers
symbol = "https://raw.githubusercontent.com/meshcloud/meshstack-hub/main/modules/aks/github-connector/buildingblock/logo.png"
target_type = "TENANT_LEVEL"
supported_platforms = [{ name = "AZURE_KUBERNETES_SERVICE" }]

readme = chomp(<<EOT
## What is it?

The **GitHub Actions AKS Connector** integrates a GitHub repository with an Azure Kubernetes Service (AKS) namespace. It sets up the necessary service accounts, secrets, and workflows so that GitHub Actions can build, push, and deploy container images to your AKS namespace automatically.

## When to use it?

This building block is ideal for teams that:

- Want to automate deployments to AKS using GitHub Actions.
- Need secure, short-lived credentials between GitHub and Azure without storing long-lived secrets.
- Prefer a standardised CI/CD setup with built-in security and container registry integration.

## Prerequisites

- A `Dockerfile` must be present in the connected GitHub repository, as the workflow will build and deploy this image.

## Resources Created

- **Kubernetes service account** with the necessary permissions in the target AKS namespace.
- **GitHub Actions environment** with secrets for cluster authentication and container registry access.
- **GitHub Actions workflow** triggered on push to deploy the application to AKS.

## Shared Responsibilities

| Responsibility | Platform Team | Application Team |
| ---------------------------------------------------------- | ------------- | ---------------- |
| Set up GitHub Actions workflows and templates | ✅ | ❌ |
| Manage AKS cluster configuration and networking | ✅ | ❌ |
| Ensure secure authentication between GitHub and Azure | ✅ | ❌ |
| Write application-specific deployment configurations | ❌ | ✅ |
| Manage Kubernetes manifests (Helm charts, Kustomize, etc.) | ❌ | ✅ |
| Monitor deployments and troubleshoot issues | ❌ | ✅ |

---
EOT
)
run_transparency = true
}

version_spec = {
draft = true
implementation = {
terraform = {
repository_url = "https://github.com/meshcloud/meshstack-hub.git"
terraform_version = "1.9.0"
async = false
ref_name = var.hub.git_ref
repository_path = "modules/aks/github-connector/buildingblock"
use_mesh_http_backend_fallback = true
}
}

dependency_refs = [
{ uuid = var.github.repo_definition_uuid }
]

inputs = {
"GITHUB_OWNER" = {
argument = jsonencode(var.github.org)
assignment_type = "STATIC"
description = "GitHub organization or user that owns the repositories managed by this building block."
display_name = "GitHub Owner"
is_environment = true
type = "STRING"
updateable_by_consumer = false
}
"GITHUB_APP_ID" = {
argument = jsonencode(var.github.app_id)
assignment_type = "STATIC"
description = "GitHub App ID used to authenticate the GitHub Terraform provider."
display_name = "GitHub App ID"
is_environment = true
type = "STRING"
updateable_by_consumer = false
}
"GITHUB_APP_INSTALLATION_ID" = {
argument = jsonencode(var.github.app_installation_id)
assignment_type = "STATIC"
description = "GitHub App Installation ID used to authenticate the GitHub Terraform provider."
display_name = "GitHub App Installation ID"
is_environment = true
type = "STRING"
updateable_by_consumer = false
}
"GITHUB_APP_PEM_FILE" = {
assignment_type = "STATIC"
description = "GitHub App PEM private key used to authenticate the GitHub Terraform provider."
display_name = "GitHub App PEM File"
is_environment = true
type = "CODE"
updateable_by_consumer = false
sensitive = {
argument = {
secret_value = var.github.app_pem_file
secret_version = sha256(var.github.app_pem_file)
}
}
}
"namespace" = {
assignment_type = "PLATFORM_TENANT_ID"
display_name = "AKS Namespace"
is_environment = false
type = "STRING"
}
"github_repo" = {
argument = jsonencode("${var.github.repo_definition_uuid}.repo_name")
assignment_type = "BUILDING_BLOCK_OUTPUT"
description = "Full name (owner/repo) of the GitHub repository to connect."
display_name = "GitHub Repository"
is_environment = false
type = "STRING"
updateable_by_consumer = false
}
"github_environment_name" = {
assignment_type = "USER_INPUT"
description = "Name of the GitHub environment to use for deployments."
display_name = "GitHub Environment Name"
default_value = jsonencode("production")
is_environment = false
type = "STRING"
updateable_by_consumer = false
}
"additional_environment_variables" = {
assignment_type = "USER_INPUT"
description = "Map of additional environment variable key/value pairs to set as GitHub Actions environment variables."
display_name = "Additional Environment Variables"
default_value = jsonencode({})
is_environment = false
type = "CODE"
updateable_by_consumer = false
}
"config.tf" = {
assignment_type = "STATIC"
description = "config.tf file injected into the building block run. Contains a kubeconfig stub (cluster server endpoint and CA certificate, no user credentials) and ACR credentials used to set up GitHub Actions secrets."
display_name = "config.tf"
is_environment = false
type = "FILE"
updateable_by_consumer = false
sensitive = {
argument = {
secret_value = local.config_tf_secret_value
secret_version = sha256(local.config_tf_secret_value)
}
}
}
}

outputs = {}

permissions = [
"TENANT_LIST",
"TENANT_SAVE",
"TENANT_DELETE",
]
}
}

output "building_block_definition_uuid" {
description = "UUID of the GitHub Actions AKS Connector building block definition. Use this to reference the definition as a dependency in compositions."
value = meshstack_building_block_definition.aks_github_connector.ref.uuid
}

output "building_block_definition_version_uuid" {
description = "UUID of the latest version of the GitHub Actions AKS Connector building block definition. Use this as building_block_definition_version_ref in building block instances."
value = meshstack_building_block_definition.aks_github_connector.version_latest.uuid
}
2 changes: 2 additions & 0 deletions modules/aks/starterkit/buildingblock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ No modules.
| <a name="input_name"></a> [name](#input\_name) | This name will be used for the created projects, app subdomain and GitHub repository. | `string` | n/a | yes |
| <a name="input_project_tags_yaml"></a> [project\_tags\_yaml](#input\_project\_tags\_yaml) | YAML configuration for project tags that will be applied to dev and prod projects. Expected structure:<pre>yaml<br>dev:<br> key1:<br> - "value1"<br> - "value2"<br> key2:<br> - "value3"<br>prod:<br> key1:<br> - "value4"<br> key2:<br> - "value5"<br> - "value6"</pre> | `string` | `"dev: {}\nprod: {}\n"` | no |
| <a name="input_repo_admin"></a> [repo\_admin](#input\_repo\_admin) | GitHub handle of the user who will be assigned as the repository admin. Delete building block definition input if not needed. | `string` | `null` | no |
| <a name="input_template_owner"></a> [template\_owner](#input\_template\_owner) | GitHub owner (org or user) of the repository template to use when creating the application repository. | `string` | `"likvid-bank"` | no |
| <a name="input_template_repo"></a> [template\_repo](#input\_template\_repo) | Name of the GitHub repository template to use when creating the application repository. | `string` | `"aks-starterkit-template"` | no |
| <a name="input_workspace_identifier"></a> [workspace\_identifier](#input\_workspace\_identifier) | n/a | `string` | n/a | yes |

## Outputs
Expand Down
Loading
Loading