> ## Documentation Index
> Fetch the complete documentation index at: https://docs.membrane.agency/llms.txt
> Use this file to discover all available pages before exploring further.

# Azure

# Self-hosting on Azure

To self-host Membrane on Azure, you need to set up Azure Blob Storage.

Everything else is the same as in the [main guide](/docs/managing-membrane/self-hosting).

## Setting up Azure Blob Storage

This guide uses terraform to manage the infrastructure.

1. Create a storage account and storage containers:

```hcl theme={null}
resource "azurerm_storage_account" "main" {
   name                             = "${var.environment}integrationapp"
   resource_group_name              = var.resource_group_name
   location                         = var.resource_group_location
   account_tier                     = "Standard"
   account_replication_type         = "LRS"
   min_tls_version                  = "TLS1_2"
   cross_tenant_replication_enabled = true

   blob_properties {
      cors_rule {
         allowed_headers    = ["*"]
         allowed_methods    = ["GET"]
         allowed_origins    = ["*"]
         exposed_headers    = ["*"]
         max_age_in_seconds = 3000
      }
   }

   tags = local.common_tags
}

# Container for temporary files
resource "azurerm_storage_container" "tmp" {
   name                  = "integration-app-${var.environment}-temp"
   storage_account_id    = azurerm_storage_account.main.id
   container_access_type = "private"
}

# Container for connectors
resource "azurerm_storage_container" "connectors" {
   name                  = "integration-app-${var.environment}-connectors"
   storage_account_id    = azurerm_storage_account.main.id
   container_access_type = "private"
}

# Note: For static website hosting, Azure automatically creates a $web container
# We'll use that container for static files instead of creating a separate one

# Static website configuration
resource "azurerm_storage_account_static_website" "main" {
   storage_account_id = azurerm_storage_account.main.id
   index_document     = "index.html"
   error_404_document = "404.html"
}

# Lifecycle management for tmp container
resource "azurerm_storage_management_policy" "tmp" {
   storage_account_id = azurerm_storage_account.main.id

   rule {
      name    = "cleanup"
      enabled = true

      filters {
         blob_types   = ["blockBlob"]
         prefix_match = ["tmp/"]
      }

      actions {
         base_blob {
            delete_after_days_since_modification_greater_than = 7
         }
      }
   }
}
```

2. Configure CDN FrontDoor for serving static files (Optional)

```hcl theme={null}
resource "azurerm_cdn_frontdoor_profile" "static" {
   name                = "${var.environment}-afd-static"
   resource_group_name = var.resource_group_name
   sku_name            = "Standard_AzureFrontDoor"

   tags = {
      Service = "core-azure"
   }
}

resource "azurerm_cdn_frontdoor_endpoint" "static" {
   name                     = "${var.environment}-afd-static-endpoint"
   cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.static.id
}

resource "azurerm_cdn_frontdoor_origin_group" "static" {
   name                     = "${var.environment}-afd-static-origin-group"
   cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.static.id

   load_balancing {
      sample_size                 = 4
      successful_samples_required = 3
   }
}

resource "azurerm_cdn_frontdoor_origin" "static" {
   name                           = "${var.environment}-afd-static-origin"
   cdn_frontdoor_origin_group_id  = azurerm_cdn_frontdoor_origin_group.static.id
   enabled                        = true
   host_name                      = "${azurerm_storage_account.main.name}.z13.web.core.windows.net"
   http_port                      = 80
   https_port                     = 443
   origin_host_header             = "${azurerm_storage_account.main.name}.z13.web.core.windows.net"
   priority                       = 1
   weight                         = 1000
   certificate_name_check_enabled = true
}

resource "azurerm_cdn_frontdoor_route" "static" {
   name                            = "${var.environment}-afd-static-route"
   cdn_frontdoor_endpoint_id       = azurerm_cdn_frontdoor_endpoint.static.id
   cdn_frontdoor_origin_group_id   = azurerm_cdn_frontdoor_origin_group.static.id
   cdn_frontdoor_origin_ids        = [azurerm_cdn_frontdoor_origin.static.id]
   enabled                         = true
   forwarding_protocol             = "HttpsOnly"
   https_redirect_enabled          = true
   patterns_to_match               = ["/*"]
   supported_protocols             = ["Http", "Https"]
   link_to_default_domain          = true
   cdn_frontdoor_custom_domain_ids = [azurerm_cdn_frontdoor_custom_domain.static.id]
   cdn_frontdoor_rule_set_ids      = [azurerm_cdn_frontdoor_rule_set.static.id]
}

resource "azurerm_cdn_frontdoor_custom_domain" "static" {
   name                     = "staticazureintmembrane"
   cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.static.id
   host_name                = "static.${var.dns_zone_name}"

   tls {
      certificate_type = "ManagedCertificate"
   }
}

resource "azurerm_cdn_frontdoor_custom_domain_association" "static" {
   cdn_frontdoor_custom_domain_id = azurerm_cdn_frontdoor_custom_domain.static.id
   cdn_frontdoor_route_ids        = [azurerm_cdn_frontdoor_route.static.id]
}

resource "azurerm_cdn_frontdoor_rule_set" "static" {
   name                     = "${var.environment}staticrules"
   cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.static.id
}

resource "azurerm_cdn_frontdoor_rule" "compression" {
   name                      = "enablecompression"
   cdn_frontdoor_rule_set_id = azurerm_cdn_frontdoor_rule_set.static.id
   order                     = 1
   behavior_on_match         = "Continue"

   conditions {
      request_method_condition {
         match_values     = ["GET"]
         operator         = "Equal"
         negate_condition = false
      }
   }

   actions {
      route_configuration_override_action {
         compression_enabled           = true
         cache_behavior                = "OverrideIfOriginMissing"
         cache_duration                = "1.00:00:00" # 1 day
         query_string_caching_behavior = "IgnoreQueryString"
      }
   }
}

resource "azurerm_cdn_frontdoor_rule" "cache_static_assets" {
   name                      = "cachestaticassets"
   cdn_frontdoor_rule_set_id = azurerm_cdn_frontdoor_rule_set.static.id
   order                     = 2
   behavior_on_match         = "Continue"

   conditions {
      request_uri_condition {
         match_values     = ["*.js", "*.css", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.svg", "*.ico", "*.woff", "*.woff2"]
         operator         = "EndsWith"
         negate_condition = false
      }
   }

   actions {
      route_configuration_override_action {
         cache_behavior                = "OverrideAlways"
         cache_duration                = "7.00:00:00" # 7 days for static assets
         query_string_caching_behavior = "IgnoreQueryString"
      }
   }
}
```

3. Configure custom domain DNS managed zone for BASE\_STATIC\_URI (Optional)

```hcl theme={null}
resource "azurerm_dns_txt_record" "afd_static_validation" {
   name                = "_dnsauth.static"
   zone_name           = var.dns_zone_name
   resource_group_name = var.resource_group_name
   ttl                 = 3600

   record {
      value = azurerm_cdn_frontdoor_custom_domain.static.validation_token
   }
}

resource "azurerm_dns_cname_record" "static" {
   name                = "static"
   zone_name           = var.dns_zone_name
   resource_group_name = var.resource_group_name
   ttl                 = 3600
   record              = azurerm_cdn_frontdoor_endpoint.static.host_name
}
```
