Resource Packs

Cloud

Example

Flavor

Feature

Blob Storage

Example: azure-blob resource based on Azure Blob Storage

Configuration

This example configures an azure-blob and an azure-blob-account Resource Definition to enable workloads to use Azure Blob Storage.

The azure-blob-account represent the Azure Storage Account and is shared across all workloads.

Workloads can use the azure-blob resource type to request Azure Storage Containers with two different access policies:

  • basic-admin (full access)
  • basic-read-only (read-only access)

From Score requesting a Azure Storage Container with full access looks like:

resources:
  ...
  storage:
    type: azure-blob
    class: basic-admin

Infrastructure setup

The workload service account will automatically be assigned the necessary Azure Role.

graph TD;
    subgraph Resource Group
        subgraph account["Azure Storage Account"]
            container["Azure Storage Container"]
        end
        k8s-service-account["K8s Service Account"] -- azure federated identity --> azure-managed-identity["Azure Managed Identity"]
        azure-managed-identity -- owner role --> container
        subgraph AKS Cluster
            workload-pod["Workload Pod"] --> k8s-service-account
            workload-pod -- operations --> container
        end
    end

Orchestrator setup

The Resource Graph is using delegator resources to expose shared resources with different access policies.

graph TD;
    workload_1 --> k8s_sa_1["k8s_service_account_1, resource_type: k8s-service-account"]:::policy 
    az_fi_1 --> az_mi_1["azure_managed_identity, resource_type: azure-managed-identity"]:::policy
    az_ra_1["azure_role_assignments, resource_type: azure-role-assignments"]:::policy --> az_mi_1
    az_ra_1 --> az_rd_1["azure_role_definition, resource_type: azure-role-definition"]:::policy
    az_fi_1["azure_federated_identity, resource_type: azure-federated-identity"]:::policy --> k8s_sa_1
    workload_1 --> blob_1["delegator_blob_1, resource_type: azure-blob"]
    az_rd_1 -- owner --> blob_1
    workload_2 --> p_2["identities & roles setup similar to workload 1"]:::policy
    workload_2 --> blob_2["delegator_blob_2, resource_type: azure-blob"]
    p_2 -- reader --> blob_2
    blob_1 --> shared.blob_1["shared.blob_1, resource_type: azure-blob"]
    blob_2 --> shared.blob_1
    shared.blob_1 --> blob_account["shared.main-blob-account, resource_type: azure-blob-account"]
    
    classDef policy fill:#f96

Terraform docs

Requirements

Name Version
terraform >= 1.3.0
azuread ~> 2.47
azurerm ~> 3.91
humanitec ~> 1.0
random ~> 3.6

Providers

Name Version
azuread ~> 2.47
azurerm ~> 3.91
humanitec ~> 1.0
random ~> 3.6

Modules

Name Source Version
azure_blob_account ../../humanitec-resource-defs/azure-blob-account/basic n/a
blob_storage ../../humanitec-resource-defs/azure-blob/basic n/a
blob_storage_admin ../../humanitec-resource-defs/azure-blob/delegator n/a
blob_storage_reader ../../humanitec-resource-defs/azure-blob/delegator n/a
federated_identity ../../humanitec-resource-defs/azure-federated-identity/basic n/a
k8s_service_account ../../humanitec-resource-defs/k8s/service-account n/a
managed_identity ../../humanitec-resource-defs/azure-managed-identity/basic n/a
role_assignment ../../humanitec-resource-defs/azure-role-assignments/basic n/a
role_definition_admin ../../humanitec-resource-defs/azure-role-definition/echo n/a
role_definition_reader ../../humanitec-resource-defs/azure-role-definition/echo n/a
workload ../../humanitec-resource-defs/workload/service-account n/a

Resources

Name Type
azuread_application.humanitec_provisioner resource
azuread_service_principal.humanitec_provisioner resource
azuread_service_principal_password.humanitec_provisioner resource
azurerm_role_assignment.resource_group resource
azurerm_storage_account.tfstate resource
azurerm_storage_container.tfstate resource
humanitec_application.example resource
humanitec_resource_account.humanitec_provisioner resource
humanitec_resource_definition_criteria.azure_blob_account resource
humanitec_resource_definition_criteria.blob_storage resource
humanitec_resource_definition_criteria.blob_storage_admin resource
humanitec_resource_definition_criteria.blob_storage_reader resource
humanitec_resource_definition_criteria.federated_identity resource
humanitec_resource_definition_criteria.k8s_service_account resource
humanitec_resource_definition_criteria.managed_identity resource
humanitec_resource_definition_criteria.role_assignment resource
humanitec_resource_definition_criteria.role_definition_admin resource
humanitec_resource_definition_criteria.role_definition_reader resource
humanitec_resource_definition_criteria.workload resource
random_string.storage_account_suffix resource
azurerm_resource_group.main data source

Inputs

Name Description Type Default Required
aks_cluster_issuer_url AKS OIDC Issuer URL string n/a yes
resource_group_name Specifies the Name of the Resource Group within which created resources will reside. string n/a yes
subscription_id The Subscription ID which should be used. string n/a yes
account_replication_type Defines the type of replication to use for this storage account. string "GRS" no
account_tier Defines the Tier to use for this storage account. string "Standard" no
container_access_type The Access Level configured for this Container. string "private" no
name Specifies the Name for created example application. string "hum-rp-blob-storage-example" no
prefix Specifies the prefix used in default name for created resources. string "hum-rp-blob-storage-ex-" no
resource_packs_azure_rev Azure Resource Pack git branch. string "refs/heads/main" no
resource_packs_azure_url Azure Resource Pack git url. string "https://github.com/humanitec-architecture/resource-packs-azure.git" no

main.tf (view on GitHub) :

# Service principal used by Humanitec to provision resources
data "azurerm_resource_group" "main" {
  name = var.resource_group_name
}

resource "azuread_application" "humanitec_provisioner" {
  display_name = var.name
}

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

resource "azuread_service_principal_password" "humanitec_provisioner" {
  service_principal_id = azuread_service_principal.humanitec_provisioner.object_id
}

resource "azurerm_role_assignment" "resource_group" {
  scope                = data.azurerm_resource_group.main.id
  role_definition_name = "Owner"
  principal_id         = azuread_service_principal.humanitec_provisioner.object_id
}

resource "humanitec_resource_account" "humanitec_provisioner" {
  id   = var.name
  name = var.name
  type = "azure"

  credentials = jsonencode({
    "appId" : azuread_service_principal.humanitec_provisioner.client_id,
    "displayName" : azuread_application.humanitec_provisioner.display_name,
    "password" : azuread_service_principal_password.humanitec_provisioner.value,
    "tenant" : azuread_service_principal.humanitec_provisioner.application_tenant_id
  })

  depends_on = [
    # Otherwise the account looses permissions before the resources are deleted
    azurerm_role_assignment.resource_group
  ]
}

# Example application and resource definition criteria

resource "humanitec_application" "example" {
  id   = var.name
  name = var.name
}

locals {
  # Define the shared azure-blob-account resource id and class
  azure_blob_account_res_id = "main-blob-account"
  azure_blob_account_class  = "default"

  # Classes used to build the resource definition graph
  blob_storage_basic_class         = "basic"
  blob_storage_admin_policy_class  = "blob-storage-basic-admin"
  blob_storage_reader_policy_class = "blob-storage-basic-reader"

  # Classes that developers can select from
  blob_storage_admin_class  = "basic-admin"
  blob_storage_reader_class = "basic-read-ony"

  account_resource   = "azure-blob-account.${local.azure_blob_account_class}#${local.azure_blob_account_res_id}"
  blob_storage_scope = "/subscriptions/${var.subscription_id}/resourceGroups/${var.resource_group_name}/providers/Microsoft.Storage/storageAccounts/$${resources['${local.account_resource}'].outputs.name}/blobServices/default/containers/$${resources['azure-blob.${local.blob_storage_basic_class}'].outputs.container}"

  # Azure build in role ids: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
  build_in_azure_storage_blob_data_owner_role_id  = "/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b"
  build_in_azure_storage_blob_data_reader_role_id = "/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1"
}

# Shared azure-blob-account resource

module "azure_blob_account" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-blob-account/basic"

  resource_packs_azure_url = var.resource_packs_azure_url
  resource_packs_azure_rev = var.resource_packs_azure_rev
  append_logs_to_error     = true
  terraform_state          = local.terraform_state
  driver_account           = humanitec_resource_account.humanitec_provisioner.id
  subscription_id          = var.subscription_id
  resource_group_name      = var.resource_group_name
  prefix                   = var.prefix
  account_tier             = var.account_tier
  account_replication_type = var.account_replication_type
}

resource "humanitec_resource_definition_criteria" "azure_blob_account" {
  resource_definition_id = module.azure_blob_account.id
  app_id                 = humanitec_application.example.id
  res_id                 = local.azure_blob_account_res_id
  class                  = local.azure_blob_account_class

  force_delete = true
}

# Workload or shared blob-storage resources

module "blob_storage" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-blob/basic"

  resource_packs_azure_url = var.resource_packs_azure_url
  resource_packs_azure_rev = var.resource_packs_azure_rev
  append_logs_to_error     = true
  terraform_state          = local.terraform_state
  driver_account           = humanitec_resource_account.humanitec_provisioner.id
  subscription_id          = var.subscription_id
  prefix                   = var.prefix
  container_access_type    = var.container_access_type
  account_resource         = local.account_resource
}

resource "humanitec_resource_definition_criteria" "blob_storage" {
  resource_definition_id = module.blob_storage.id
  app_id                 = humanitec_application.example.id
  class                  = local.blob_storage_basic_class

  force_delete = true
}

// Admin shared

// Exposed delegator resource definition
module "blob_storage_admin" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-blob/delegator"

  prefix                      = "${var.prefix}admin-"
  policy_resource_class       = local.blob_storage_admin_policy_class
  blob_storage_resource_class = local.blob_storage_basic_class
}

resource "humanitec_resource_definition_criteria" "blob_storage_admin" {
  resource_definition_id = module.blob_storage_admin.id
  app_id                 = humanitec_application.example.id
  class                  = local.blob_storage_admin_class

  force_delete = true
}

module "role_definition_admin" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-role-definition/echo"

  prefix = "${var.prefix}admin-"

  role_definition_id    = local.build_in_azure_storage_blob_data_owner_role_id
  role_definition_scope = local.blob_storage_scope
}

resource "humanitec_resource_definition_criteria" "role_definition_admin" {
  resource_definition_id = module.role_definition_admin.id
  app_id                 = humanitec_application.example.id
  class                  = local.blob_storage_admin_policy_class

  force_delete = true
}

// Reader shared

// Exposed delegator resource definition
module "blob_storage_reader" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-blob/delegator"

  prefix                      = "${var.prefix}reader-"
  policy_resource_class       = local.blob_storage_reader_policy_class
  blob_storage_resource_class = local.blob_storage_basic_class
}

resource "humanitec_resource_definition_criteria" "blob_storage_reader" {
  resource_definition_id = module.blob_storage_reader.id
  app_id                 = humanitec_application.example.id
  class                  = local.blob_storage_reader_class

  force_delete = true
}

module "role_definition_reader" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-role-definition/echo"

  prefix = "${var.prefix}reader-"

  role_definition_id    = local.build_in_azure_storage_blob_data_reader_role_id
  role_definition_scope = local.blob_storage_scope
}

resource "humanitec_resource_definition_criteria" "role_definition_reader" {
  resource_definition_id = module.role_definition_reader.id
  app_id                 = humanitec_application.example.id
  class                  = local.blob_storage_reader_policy_class

  force_delete = true
}

// Workload based

module "workload" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/workload/service-account"

  prefix = var.prefix
}

resource "humanitec_resource_definition_criteria" "workload" {
  resource_definition_id = module.workload.id
  app_id                 = humanitec_application.example.id

  force_delete = true
}

module "k8s_service_account" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/k8s/service-account"

  prefix = var.prefix
}

resource "humanitec_resource_definition_criteria" "k8s_service_account" {
  resource_definition_id = module.k8s_service_account.id
  app_id                 = humanitec_application.example.id

  force_delete = true
}

module "federated_identity" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-federated-identity/basic"

  resource_packs_azure_url = var.resource_packs_azure_url
  resource_packs_azure_rev = var.resource_packs_azure_rev
  append_logs_to_error     = true
  terraform_state          = local.terraform_state
  driver_account           = humanitec_resource_account.humanitec_provisioner.id
  subscription_id          = var.subscription_id

  prefix = var.prefix

  resource_group_name = var.resource_group_name
  audience            = ["api://AzureADTokenExchange"]
  issuer              = var.aks_cluster_issuer_url
  parent_id           = "$${resources.azure-managed-identity.outputs.id}"
  subject             = "system:serviceaccount:$${resources.k8s-namespace.outputs.namespace}:$${resources.k8s-service-account.outputs.name}"
}

resource "humanitec_resource_definition_criteria" "federated_identity" {
  resource_definition_id = module.federated_identity.id
  app_id                 = humanitec_application.example.id

  force_delete = true
}

module "managed_identity" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-managed-identity/basic"

  resource_packs_azure_url = var.resource_packs_azure_url
  resource_packs_azure_rev = var.resource_packs_azure_rev
  append_logs_to_error     = true
  terraform_state          = local.terraform_state
  driver_account           = humanitec_resource_account.humanitec_provisioner.id
  subscription_id          = var.subscription_id

  prefix              = var.prefix
  resource_group_name = var.resource_group_name
}

resource "humanitec_resource_definition_criteria" "managed_identity" {
  resource_definition_id = module.managed_identity.id
  app_id                 = humanitec_application.example.id

  force_delete = true
}

module "role_assignment" {
  source = "github.com/humanitec-architecture/resource-packs-azure?ref=v2024-06-14//humanitec-resource-defs/azure-role-assignments/basic"

  resource_packs_azure_url = var.resource_packs_azure_url
  resource_packs_azure_rev = var.resource_packs_azure_rev
  append_logs_to_error     = true
  terraform_state          = local.terraform_state
  driver_account           = humanitec_resource_account.humanitec_provisioner.id
  subscription_id          = var.subscription_id

  prefix              = var.prefix
  role_definition_ids = "$${resources.workload>azure-role-definition.outputs.id}"
  scopes              = "$${resources.workload>azure-role-definition.outputs.scope}"
  principal_id        = "$${resources.azure-managed-identity.outputs.principal_id}"
}

resource "humanitec_resource_definition_criteria" "role_assignment" {
  resource_definition_id = module.role_assignment.id
  app_id                 = humanitec_application.example.id

  force_delete = true
}


providers.tf (view on GitHub) :

terraform {
  required_providers {
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.47"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.91"
    }
    humanitec = {
      source  = "humanitec/humanitec"
      version = "~> 1.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }

  required_version = ">= 1.3.0"
}

provider "humanitec" {
}

provider "azuread" {
}

provider "azurerm" {
  features {}

  subscription_id = var.subscription_id
}


state.tf (view on GitHub) :

# Resources required for the terraform backend
# More details https://developer.humanitec.com/integration-and-extensions/drivers/generic-drivers/terraform/

resource "random_string" "storage_account_suffix" {
  length  = 12
  special = false
  upper   = false
}

resource "azurerm_storage_account" "tfstate" {
  name                            = "humrp${random_string.storage_account_suffix.result}tfstate"
  resource_group_name             = data.azurerm_resource_group.main.name
  location                        = data.azurerm_resource_group.main.location
  account_tier                    = "Standard"
  account_replication_type        = "ZRS"
  allow_nested_items_to_be_public = false
}

resource "azurerm_storage_container" "tfstate" {
  name                  = var.name
  storage_account_name  = azurerm_storage_account.tfstate.name
  container_access_type = "private"
}

locals {
  terraform_state = {
    subscription_id      = var.subscription_id
    resource_group_name  = data.azurerm_resource_group.main.name
    storage_account_name = azurerm_storage_account.tfstate.name
    container_name       = azurerm_storage_container.tfstate.name
    # key_prefix is set by the respective resource definition
  }
}


terraform.tfvars.example (view on GitHub) :


# Defines the type of replication to use for this storage account.
account_replication_type = "GRS"

# Defines the Tier to use for this storage account.
account_tier = "Standard"

# AKS OIDC Issuer URL
aks_cluster_issuer_url = ""

# The Access Level configured for this Container.
container_access_type = "private"

# Specifies the Name for created example application.
name = "hum-rp-blob-storage-example"

# Specifies the prefix used in default name for created resources.
prefix = "hum-rp-blob-storage-ex-"

# Specifies the Name of the Resource Group within which created resources will reside.
resource_group_name = ""

# Azure Resource Pack git branch.
resource_packs_azure_rev = "refs/tags/v2024-06-14"

# Azure Resource Pack git url.
resource_packs_azure_url = "https://github.com/humanitec-architecture/resource-packs-azure.git"

# The Subscription ID which should be used.
subscription_id = ""

variables.tf (view on GitHub) :

variable "resource_packs_azure_url" {
  description = "Azure Resource Pack git url."
  type        = string
  default     = "https://github.com/humanitec-architecture/resource-packs-azure.git"
}

variable "resource_packs_azure_rev" {
  description = "Azure Resource Pack git branch."
  type        = string
  default     = "refs/tags/v2024-06-14"
}

variable "subscription_id" {
  description = "The Subscription ID which should be used."
  type        = string
}

variable "resource_group_name" {
  description = "Specifies the Name of the Resource Group within which created resources will reside."
  type        = string
}

variable "name" {
  description = "Specifies the Name for created example application."
  type        = string
  default     = "hum-rp-blob-storage-example"
}

variable "prefix" {
  description = "Specifies the prefix used in default name for created resources."
  type        = string
  default     = "hum-rp-blob-storage-ex-"
}

variable "account_tier" {
  description = "Defines the Tier to use for this storage account."
  type        = string
  default     = "Standard"
}

variable "account_replication_type" {
  description = "Defines the type of replication to use for this storage account."
  type        = string
  default     = "GRS"
}

variable "container_access_type" {
  description = "The Access Level configured for this Container."
  type        = string
  default     = "private"
}

variable "aks_cluster_issuer_url" {
  description = "AKS OIDC Issuer URL"
  type        = string
}

Top