Azure components¶
All components within an Azure-based MACH composer configuration are
automatically considered to have a 'azure' integration by default. Only if
'azure' is explicitly omitted from the integrations
definition, it won't
require any Azure-specific variables.
To be able to create the resources needed, a couple of extra Terraform variables are set by MACH composer.
In addition to this, the component itself is responsible for packaging and deploying the correct assets in case of a Function App.
Terraform variables¶
In addition to the base variables, an Azure component expects the following:
variable "azure_short_name" {
type = string
description = "Short name; will not be more than 10 characters to prevent Azure naming limits"
}
variable "azure_name_prefix" {
type = string
description = "Name prefix to be used for all Azure resources"
}
variable "azure_subscription_id" {
type = string
description = "The current subscription ID"
}
variable "azure_tenant_id" {
type = string
description = "The current tenant ID"
}
variable "azure_service_object_ids" {
type = map(string)
description = "User/Group/Principle IDs that should be able to access for example a KeyVault."
}
variable "azure_region" {
type = string
description = "Azure region"
}
variable "azure_resource_group" {
type = object({
name = string
location = string
})
description = "Information of the resource group the component should be created in"
}
variable "azure_monitor_action_group_id" {
type = string
default = "Action group ID when alert group is configured.
}
Monitor action group
monitor_action_group_id
is set to the
action group
ID when an alert_group is configured.
With endpoints
¶
In order to support the endpoints
attribute on the component, the component needs to define what endpoints it
expects and needs to provide output about the
routing options.
For example, if the component requires two endpoints (main
and webhooks
) to
be set, the following variables and outputs needs to be defined:
variable "azure_endpoint_main" {
type = object({
url = string
frontdoor_id = string
})
}
variable "azure_endpoint_webhooks" {
type = object({
url = string
frontdoor_id = string
})
}
output "azure_endpoint_main" {
value = {
address = azurerm_app_service.main.default_site_hostname
}
}
output "azure_endpoint_webhooks" {
value = {
address = azurerm_app_service.main.default_site_hostname
routes = [
{
name = "hooks"
patterns = [
"/hooks/*",
]
},
]
}
}
Defining outputs
¶
As shown in the example above, the component needs to have an output per endpoint defined in order to instruct MACH how to set up Frontdoor routing.
This output needs to have a name in the form of azure_endpoint_<endpoint-name>
and contain the following attributes:
address
- (Required) The host address to route traffic tohost_header
- The value to use as the host header sent to the backend. By default, will take the value ofaddress
.http_port
- The HTTP TCP port number. Possible values are between1
-65535
. Defaults to80
.https_port
- The HTTPS TCP port number. Possible values are between1
-65535
. Defaults to443
.health_probe_path
- The path to use for the Health Probe. If left empty, health probe won't be enabled.health_probe_protocol
- Protocol scheme to use for the Health Probe. Defaults toHttp
health_probe_method
- Specifies HTTP method the health probe uses when querying the service. Possible values include:GET
andHEAD
. Defaults toGET
.routes
- A list of custom Frontdoor routing rules. By default, MACH will generate one default routing rule for each component.
routes
options
patterns
- List of patterns to matchcache_enabled
- Specifies whether to Enable caching or not. Valid options aretrue
orfalse
. Defaults tofalse
.custom_forwarding_path
- Path to use when constructing the request to forward to the backend. This functions as a URL Rewrite. Default behaviour preserves the URL path.
Full example
output "azure_endpoint_main" {
value = {
address = azurerm_app_service.main.default_site_hostname
health_probe_path = "/"
health_probe_method = "Get"
host_header = "www.example.com/something-else/"
https_port = 9000
routes = [
{
patterns = [
"/*",
]
},
{
patterns = [
"/media/*",
]
cache_enabled = true
}
]
}
}
With service_plan
¶
When a component has been configured with a service_plan
,
MACH composer manages the service plan for you and passes the information to the
component with a app_service_plan
variable:
variable "azure_app_service_plan" {
type = object({
id = string
name = string
resource_group_name = string
})
}
Packaging and deploying¶
For Azure functions, the deployment process consists of two steps:
- Packaging the function
- Deploying it to the function app storage
Read more about Azure component deployments.
Configure runtime¶
When defining your Azure function app resource, you can reference back to the asset that is deployed:
data "azurerm_storage_account" "shared" {
name = "mysharedwesacomponents"
resource_group_name = "my-shared-we-rg"
}
locals {
package_name = format("yourcomponent-%s.zip", var.component_version)
}
resource "azurerm_function_app" "your_component" {
app_settings = {
WEBSITE_RUN_FROM_ZIP = "https://${data.azurerm_storage_account.shared.name}.blob.core.windows.net/code/${local.package_name}${data.azurerm_storage_account_blob_container_sas.code_access.sas}"
...
}
}
HTTP routing¶
MACH composer will provide the correct HTTP routing for you.
To do so, the following has to be configured:
Default endpoint
If you assign default
to one of your components endpoints, no additional
Frontdoor settings are needed.
MACH composer will create a Frontdoor instance for you without any custom domain.
More information in the deployment section.
Naming conventions¶
When setting up Terraform components for Azure we need to follow the following naming conventions and mind the current naming restrictions.
An important thing to highlight is the following:
Resource names have length limits. Balancing the context embedded in a name with its scope and length is important when you develop your naming conventions. For more information about naming rules for allowed characters, scopes, and name lengths for resource types, see Naming conventions for Azure resources.
Resource prefixing¶
MACH composer creates a name prefix which can be used to name all other resources.
This prefix is built up using
- The configured resources prefix
- The site identifier
- Region
So for example, when creating a function app, we can define this as:
resource "azurerm_function_app" "example_component" {
name = lower(format("%s-func-%s", var.azure_name_prefix, var.azure_short_name))
}
With resources_prefix
configured as mach-
and a site id of uk-example-prd
(UK site of Example site on production) the result will be
mach-uk-example-p-we-func-example
.
To save character space [^1], the following adjustments are made:
- The values
tst
,dev
andprd
will be shortened tot
,d
, andp
- The region is shortened to two characters. For example:
westeurope
becomeswe
Azure naming restrictions¶
Some resources might have a maximum length to be used as name.
To work with these limitations, the short_name
variable can be used.
For example, a Storage Account can be created using
resource "azurerm_storage_account" "main" {
name = replace(lower(format("%s-sa-%s", var.azure_name_prefix, var.azure_short_name)), "-", "")
...
}
Where sa
stands for 'storage account'
WARNING: Some resources have a name restriction of max 24 characters. Obviously we want to avoid hitting that limit. See Azure naming restrictions on how to avoid that.
Function App¶
We recommend using the azurerm_function_app
resource "azurerm_function_app" "example_component" {
name = lower(format("%s-func-%s", var.azure_name_prefix, var.azure_short_name))
location = var.azure_resource_group.location
resource_group_name = var.azure_resource_group.name
app_service_plan_id = var.azure_app_service_plan.id
storage_account_name = azurerm_storage_account.main.name
storage_account_access_key = azurerm_storage_account.main.primary_access_key
app_settings = local.environment_variables
os_type = "linux"
version = "~3"
https_only = true
site_config {
linux_fx_version = "PYTHON|3.8"
}
identity {
type = "SystemAssigned"
}
tags = var.tags
}