Skip to content

Add Azure Support to bufstream terraform deployment #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
gen/
**/.terraform
**/.terraform.lock.hcl
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
This repository compliments [Bufstream's documentation](https://buf.build/docs/bufstream/).

Within, you'll find a collection of modules that assist with deploying a Kubernetes cluster in
either Amazon Web Services (AWS) or Google Cloud Platform (GCP). These modules generate necessary resources
Amazon Web Services (AWS), Google Cloud Platform (GCP) or Microsoft Azure. These modules generate necessary resources
and then deploy Bufstream. The cluster created is meant to be used as a demo environment for those who would like
to test Bufstream but don't have an existing Kubernetes cluster.

Expand All @@ -15,6 +15,8 @@ To run it, you need to create a `tfvars` file that includes the required Terrafo
See below for the required variables. There is a README for each module with more details on the
variables that can be set.

Note that you'll also need to include a provider under the folder of the desired cloud.

Required environment variables:

| Variable | Description |
Expand Down Expand Up @@ -84,3 +86,21 @@ Recommended variables in `tfvars`:
| Variable | Description |
|--------------|--------------------------|
| cluster_name | Name for the GKE cluster |

## Azure

By default, the module creates all resources necessary to deploy a Kubernetes cluster to the desired project.
It also creates some specific resources required for Bufstream: a storage account and container, a virtual network
and required subnets, and the bufstream identity with its required role assignment to access storage.

Required variables in `tfvars`:

| Variable | Description |
|-------------|-------------------------------|
| location | Where to deploy the resources |

Recommended variables in `tfvars`:

| Variable | Description |
|--------------|--------------------------|
| cluster_name | Name for the AKS cluster |
10 changes: 0 additions & 10 deletions aws/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ resource "aws_lb" "bufstream" {
}

locals {
context = module.kubernetes.cluster_arn

bufstream_values = templatefile("${path.module}/bufstream.yaml.tpl", {
region = var.region
bucket_name = module.storage.bucket_ref
Expand All @@ -84,14 +82,6 @@ locals {
})
}

resource "local_file" "context" {
count = var.generate_config_files_path != null ? 1 : 0
content = local.context
filename = "${var.generate_config_files_path}/context"

file_permission = "0600"
}

resource "local_file" "bufstream_values" {
count = var.generate_config_files_path != null ? 1 : 0
content = local.bufstream_values
Expand Down
73 changes: 73 additions & 0 deletions azure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) | ~> 4.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_azurerm"></a> [azurerm](#provider\_azurerm) | 4.26.0 |
| <a name="provider_local"></a> [local](#provider\_local) | 2.5.2 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_kubernetes"></a> [kubernetes](#module\_kubernetes) | ./kubernetes | n/a |
| <a name="module_network"></a> [network](#module\_network) | ./network | n/a |
| <a name="module_storage"></a> [storage](#module\_storage) | ./storage | n/a |

## Resources

| Name | Type |
|------|------|
| [azurerm_resource_group.rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource |
| [azurerm_resource_provider_registration.registrations](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_provider_registration) | resource |
| [local_file.bufstream_values](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [local_file.kubeconfig](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [azurerm_resource_group.rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_bufstream_identity_create"></a> [bufstream\_identity\_create](#input\_bufstream\_identity\_create) | Whether to create a new Azure bufstream identity. | `bool` | `true` | no |
| <a name="input_bufstream_identity_name"></a> [bufstream\_identity\_name](#input\_bufstream\_identity\_name) | Name of Azure bufstream identity. | `string` | `"bufstream"` | no |
| <a name="input_bufstream_k8s_namespace"></a> [bufstream\_k8s\_namespace](#input\_bufstream\_k8s\_namespace) | Bufstream Kubernetes Service Account Namespace to use if enabling workload identity federation. | `string` | `"bufstream"` | no |
| <a name="input_cluster_create"></a> [cluster\_create](#input\_cluster\_create) | Whether to create a new AKS cluster. | `bool` | `true` | no |
| <a name="input_cluster_dns_service_ip"></a> [cluster\_dns\_service\_ip](#input\_cluster\_dns\_service\_ip) | DNS Service IP. Must be within services\_subnet\_cidr. | `string` | `"10.192.4.10"` | no |
| <a name="input_cluster_grant_admin_to_caller"></a> [cluster\_grant\_admin\_to\_caller](#input\_cluster\_grant\_admin\_to\_caller) | Grant admin role permission to the TF running actor. | `bool` | `true` | no |
| <a name="input_cluster_name"></a> [cluster\_name](#input\_cluster\_name) | Name of AKS cluster to create or use. | `string` | `"bufstream"` | no |
| <a name="input_cluster_subnet_cidr"></a> [cluster\_subnet\_cidr](#input\_cluster\_subnet\_cidr) | CIDR of cluster subnet in the VPC. | `string` | `"10.192.0.0/23"` | no |
| <a name="input_cluster_subnet_create"></a> [cluster\_subnet\_create](#input\_cluster\_subnet\_create) | Whether to create a cluster subnet in the VPC. | `bool` | `true` | no |
| <a name="input_cluster_subnet_name"></a> [cluster\_subnet\_name](#input\_cluster\_subnet\_name) | Name of cluster subnet in the VPC. | `string` | `"bufstream-cluster"` | no |
| <a name="input_cluster_vm_size"></a> [cluster\_vm\_size](#input\_cluster\_vm\_size) | Cluster VM size. | `string` | `"Standard_D4as_v5"` | no |
| <a name="input_generate_config_files_path"></a> [generate\_config\_files\_path](#input\_generate\_config\_files\_path) | If present, generate config files for bufstream values, kubeconfig and the context name at the selected path. | `string` | `null` | no |
| <a name="input_kubernetes_version"></a> [kubernetes\_version](#input\_kubernetes\_version) | Kubernetes version to use. | `string` | `"1.31"` | no |
| <a name="input_location"></a> [location](#input\_location) | Where to deploy the resources. | `string` | `"centralus"` | no |
| <a name="input_pods_subnet_cidr"></a> [pods\_subnet\_cidr](#input\_pods\_subnet\_cidr) | CIDR of the pods subnet in the VPC. | `string` | `"10.192.2.0/23"` | no |
| <a name="input_pods_subnet_create"></a> [pods\_subnet\_create](#input\_pods\_subnet\_create) | Whether to create a pods subnet in the VPC. | `bool` | `true` | no |
| <a name="input_pods_subnet_name"></a> [pods\_subnet\_name](#input\_pods\_subnet\_name) | Name of pods subnet in the VPC. | `string` | `"bufstream-pods"` | no |
| <a name="input_resource_group_create"></a> [resource\_group\_create](#input\_resource\_group\_create) | Whether to create a new resource group. | `bool` | `true` | no |
| <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name) | Name of new resource group to create or use. | `string` | `"bufstream"` | no |
| <a name="input_services_subnet_cidr"></a> [services\_subnet\_cidr](#input\_services\_subnet\_cidr) | Services CIDR. It is auto-created with the cluster if cluster\_create is true. | `string` | `"10.192.4.0/23"` | no |
| <a name="input_storage_account_create"></a> [storage\_account\_create](#input\_storage\_account\_create) | Whether to create a new storage account. | `string` | `true` | no |
| <a name="input_storage_account_name"></a> [storage\_account\_name](#input\_storage\_account\_name) | Name of the storage account. | `string` | `"bufstream"` | no |
| <a name="input_storage_container_create"></a> [storage\_container\_create](#input\_storage\_container\_create) | Whether to create a new storage container. | `string` | `true` | no |
| <a name="input_storage_container_name"></a> [storage\_container\_name](#input\_storage\_container\_name) | Name of the storage container. | `string` | `"bufstream"` | no |
| <a name="input_storage_grant_permissions"></a> [storage\_grant\_permissions](#input\_storage\_grant\_permissions) | Whether to grant necessary permissions on the storage account for the bufstream identity. | `string` | `true` | no |
| <a name="input_storage_kind"></a> [storage\_kind](#input\_storage\_kind) | Storage account kind. | `string` | `"StorageV2"` | no |
| <a name="input_storage_large_file_share_enabled"></a> [storage\_large\_file\_share\_enabled](#input\_storage\_large\_file\_share\_enabled) | Storage Large file share enabled. | `bool` | `false` | no |
| <a name="input_storage_replication_type"></a> [storage\_replication\_type](#input\_storage\_replication\_type) | Storage account replication type. | `string` | `"LRS"` | no |
| <a name="input_storage_tier"></a> [storage\_tier](#input\_storage\_tier) | Storage account tier. | `string` | `"Standard"` | no |
| <a name="input_vpc_cidr"></a> [vpc\_cidr](#input\_vpc\_cidr) | CIDR of new VPC to create or use. | `string` | `"10.192.0.0/16"` | no |
| <a name="input_vpc_create"></a> [vpc\_create](#input\_vpc\_create) | Whether to create a new VPC. | `bool` | `true` | no |
| <a name="input_vpc_name"></a> [vpc\_name](#input\_vpc\_name) | Name of new VPC to create or use. | `string` | `"bufstream"` | no |
| <a name="input_wif_bufstream_k8s_service_account"></a> [wif\_bufstream\_k8s\_service\_account](#input\_wif\_bufstream\_k8s\_service\_account) | Bufstream Kubernetes Service Account Name to use if enabling workload identity federation. | `string` | `"bufstream-service-account"` | no |
| <a name="input_wif_create"></a> [wif\_create](#input\_wif\_create) | Whether to enable workload identity federation. | `string` | `true` | no |

## Outputs

No outputs.
18 changes: 18 additions & 0 deletions azure/bufstream.yaml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
storage:
use: azure
azure:
bucket: ${container_name}
endpoint: https://${account_name}.blob.core.windows.net
bufstream:
deployment:
podLabels:
azure.workload.identity/use: "true"
serviceAccount:
annotations:
azure.workload.identity/client-id: ${bufstream_identity}
metadata:
use: etcd
etcd:
addresses:
- host: "bufstream-etcd.bufstream.svc.cluster.local"
port: 2379
30 changes: 30 additions & 0 deletions azure/kubeconfig.yaml.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: ${cluster_certificate}
server: ${cluster_host}
name: aks_${resource_group_name}_${cluster_name}
contexts:
- context:
cluster: aks_${resource_group_name}_${cluster_name}
user: clusterAdmin_${resource_group_name}_${cluster_name}
name: aks_${resource_group_name}_${cluster_name}
current-context: aks_${resource_group_name}_${cluster_name}
users:
- name: clusterAdmin_${resource_group_name}_${cluster_name}
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- get-token
- --login
- azurecli
- --server-id
- 6dae42f8-4368-4678-94ff-3960e28e3630
command: kubelogin
env: null
installHint: |
kubelogin is not installed, which is required to connect to AAD enabled cluster.
To learn more, go to https://aka.ms/aks/kubelogin
provideClusterInfo: false
124 changes: 124 additions & 0 deletions azure/kubernetes/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
locals {
cluster_ref = var.cluster_create ? azurerm_kubernetes_cluster.cluster[0] : data.azurerm_kubernetes_cluster.cluster[0]
bufstream_id_ref = var.bufstream_identity_create ? azurerm_user_assigned_identity.bufstream[0] : data.azurerm_user_assigned_identity.bufstream[0]
}

data "azurerm_client_config" "current" {}

resource "azurerm_kubernetes_cluster" "cluster" {
count = var.cluster_create ? 1 : 0

name = var.cluster_name
resource_group_name = var.resource_group_name
location = var.location

dns_prefix = var.resource_group_name

kubernetes_version = var.kubernetes_version

sku_tier = "Standard"

network_profile {
network_plugin = "azure"
network_policy = "cilium"
network_data_plane = "cilium"

service_cidrs = var.cluster_service_cidrs
dns_service_ip = var.cluster_dns_service_ip
}

default_node_pool {
name = "default"
temporary_name_for_rotation = "defaulttmp"

vm_size = var.cluster_vm_size

auto_scaling_enabled = true
min_count = 1
max_count = 3

vnet_subnet_id = var.cluster_vnet_subnet_id
pod_subnet_id = var.cluster_pod_subnet_id

os_sku = "AzureLinux"

// Defaults, if not set, causes changes after initial creation
upgrade_settings {
drain_timeout_in_minutes = 0
max_surge = "10%"
node_soak_duration_in_minutes = 0
}
}

automatic_upgrade_channel = "stable"
node_os_upgrade_channel = "NodeImage"

# Enable AKS Managed Entra-ID authentication, with Azure RBAC
azure_active_directory_role_based_access_control {
tenant_id = data.azurerm_client_config.current.tenant_id
azure_rbac_enabled = true
}

oidc_issuer_enabled = true
role_based_access_control_enabled = true
local_account_disabled = true

workload_identity_enabled = true

# Disable legacy http application routing
http_application_routing_enabled = false

identity {
type = "SystemAssigned"
}

run_command_enabled = true

lifecycle {
ignore_changes = [
kubernetes_version,
]
}
}

resource "azurerm_role_assignment" "bufstream" {
count = var.cluster_grant_admin_to_caller ? 1 : 0

scope = local.cluster_ref.id
role_definition_name = "Azure Kubernetes Service RBAC Cluster Admin"
principal_id = data.azurerm_client_config.current.object_id
}

data "azurerm_kubernetes_cluster" "cluster" {
count = var.cluster_create ? 0 : 1

name = var.cluster_name
resource_group_name = var.resource_group_name
}

resource "azurerm_user_assigned_identity" "bufstream" {
count = var.bufstream_identity_create ? 1 : 0

name = var.bufstream_identity_name
resource_group_name = var.resource_group_name
location = var.location
}

data "azurerm_user_assigned_identity" "bufstream" {
count = var.bufstream_identity_create ? 0 : 1

name = var.bufstream_identity_name
resource_group_name = var.resource_group_name
}

resource "azurerm_federated_identity_credential" "federated_credential" {
count = var.wif_create ? 1 : 0

name = "bufstream"
resource_group_name = var.resource_group_name

parent_id = local.bufstream_id_ref.id
audience = ["api://AzureADTokenExchange"]
issuer = local.cluster_ref.oidc_issuer_url
subject = "system:serviceaccount:${var.wif_bufstream_k8s_namespace}:${var.wif_bufstream_k8s_service_account}"
}
39 changes: 39 additions & 0 deletions azure/kubernetes/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
output "bufstream_identity" {
description = "Bufstream Identity ID"
value = local.bufstream_id_ref
}

output "cluster_name" {
description = "Container Cluster Endpoint"
value = local.cluster_ref.name
}

output "cert" {
description = "Container Cluster Certificate"
value = local.cluster_ref.kube_config[0].cluster_ca_certificate
}

output "endpoint" {
description = "Container Cluster Endpoint"
value = local.cluster_ref.kube_config[0].host
}

output "client_cert" {
description = "Container Cluster Client Certificate"
value = local.cluster_ref.kube_config[0].client_certificate
}

output "client_key" {
description = "Container Cluster Client Key"
value = local.cluster_ref.kube_config[0].client_key
}

output "admin_user" {
description = "Container Cluster Admin User"
value = local.cluster_ref.kube_config[0].username
}

output "admin_password" {
description = "Container Cluster Admin Password"
value = local.cluster_ref.kube_config[0].password
}
Loading