Skip to content

claranet/terraform-azurerm-cdn-frontdoor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Azure CDN FrontDoor

Changelog Notice Apache V2 License OpenTofu Registry

This Terraform module is designed to create an Azure CDN FrontDoor (Standard/Premium) resource.

Global versioning rule for Claranet Azure modules

Module version Terraform version OpenTofu version AzureRM version
>= 8.x.x Unverified 1.8.x >= 4.0
>= 7.x.x 1.3.x >= 3.0
>= 6.x.x 1.x >= 3.0
>= 5.x.x 0.15.x >= 2.0
>= 4.x.x 0.13.x / 0.14.x >= 2.0
>= 3.x.x 0.12.x >= 2.0
>= 2.x.x 0.12.x < 2.0
< 2.x.x 0.11.x < 2.0

Contributing

If you want to contribute to this repository, feel free to use our pre-commit git hook configuration which will help you automatically update and format some files for you by enforcing our Terraform code module best-practices.

More details are available in the CONTRIBUTING.md file.

Usage

This module is optimized to work with the Claranet terraform-wrapper tool which set some terraform variables in the environment needed by this module. More details about variables set by the terraform-wrapper available in the documentation.

⚠️ Since modules version v8.0.0, we do not maintain/check anymore the compatibility with Hashicorp Terraform. Instead, we recommend to use OpenTofu.

module "azure_region" {
  source  = "claranet/regions/azurerm"
  version = "x.x.x"

  azure_region = var.azure_region
}

module "rg" {
  source  = "claranet/rg/azurerm"
  version = "x.x.x"

  location    = module.azure_region.location
  client_name = var.client_name
  environment = var.environment
  stack       = var.stack
}

module "logs" {
  source  = "claranet/run/azurerm//modules/logs"
  version = "x.x.x"

  client_name         = var.client_name
  environment         = var.environment
  stack               = var.stack
  location            = module.azure_region.location
  location_short      = module.azure_region.location_short
  resource_group_name = module.rg.resource_group_name
}

# NOTE: In order for the certificate to be used by Azure FrontDoor, it must be PKCS#12 PFX 3DES.
# The PFX must only contain the leaf and any intermediates, but it must not contain any Root CAs
# already trusted by Azure. openssl v3 requires -legacy flag for 3DES compatibility.
# Generate the CSR, get it signed by the CA, then create the PFX.
#
# openssl pkcs12 -export -out cert.pfx -inkey leaf.key -in leaf.pem -certfile intermediate.pem -legacy
#
resource "azurerm_key_vault_certificate" "cert" {
  name         = "custom-contoso-com"
  key_vault_id = var.key_vault_id

  certificate {
    contents = "abcd" # filebase64("./cert.pfx")
    password = ""
  }

  # The following is required for PFX imports, but not PEM.
  certificate_policy {
    issuer_parameters {
      name = "Unknown"
    }
    key_properties {
      exportable = true
      key_size   = 2048
      key_type   = "RSA"
      reuse_key  = false
    }
    secret_properties {
      content_type = "application/x-pkcs12"
    }
  }

}

module "cdn_frontdoor" {
  source  = "claranet/cdn-frontdoor/azurerm"
  version = "x.x.x"

  client_name = var.client_name
  environment = var.environment
  stack       = var.stack

  resource_group_name = module.rg.resource_group_name

  sku_name = "Premium_AzureFrontDoor"

  logs_destinations_ids = [
    module.logs.log_analytics_workspace_id,
    module.logs.logs_storage_account_id,
  ]

  endpoints = [
    {
      name = "web"
    },
    {
      name    = "azure"
      enabled = false
    },
  ]

  origin_groups = [
    {
      name = "contoso"
      health_probe = {
        interval_in_seconds = 250
        path                = "/"
        protocol            = "Https"
        request_type        = "GET"
      }
      load_balancing = {
        successful_samples_required = 1
      }
    },
    {
      name = "contoso2"
      health_probe = {
        interval_in_seconds = 250
        path                = "/"
        protocol            = "Https"
        request_type        = "GET"
      }
    },
  ]

  origins = [
    {
      name                           = "web"
      origin_group_name              = "contoso"
      certificate_name_check_enabled = false
      host_name                      = "www.contoso.com"
    },
    {
      name                           = "azure"
      origin_group_name              = "contoso2"
      certificate_name_check_enabled = false
      host_name                      = "azure.contoso.com"
    },
  ]

  custom_domains = [
    {
      name      = "www"
      host_name = "www.contoso.com"
    },
    {
      name      = "custom-contoso-com"
      host_name = "custom.contoso.com"
      tls = {
        certificate_type         = "CustomerCertificate"
        key_vault_certificate_id = azurerm_key_vault_certificate.cert.id
      }
    }
  ]

  routes = [
    {
      name                 = "route66"
      endpoint_name        = "web"
      origin_group_name    = "contoso"
      origins_names        = ["web", "azure"]
      forwarding_protocol  = "HttpsOnly"
      patterns_to_match    = ["/*"]
      supported_protocols  = ["Http", "Https"]
      custom_domains_names = ["www"]
      rule_sets_names      = ["my_rule_set", "my_rule_set2"]
    },
    {
      name                = "route2"
      endpoint_name       = "azure"
      origin_group_name   = "contoso2"
      origins_names       = ["web"]
      forwarding_protocol = "HttpsOnly"
      patterns_to_match   = ["/contoso"]
      supported_protocols = ["Http", "Https"]
      rule_sets_names     = ["my_rule_set", "my_rule_set2"]
    },
  ]

  rule_sets = [
    {
      name                 = "my_rule_set"
      custom_resource_name = "custom_rule"

      rules = [{
        name                 = "redirect"
        custom_resource_name = "myrulename"
        order                = 1
        actions = {
          response_header_actions = [
            {
              header_action = "Overwrite"
              header_name   = "Access-Control-Allow-Origin"
              value         = "https://www.foo.bar.fr"
            },
            {
              header_action = "Overwrite"
              header_name   = "Access-Control-Allow-Credentials"
              value         = "true"
            },
            {
              header_action = "Overwrite"
              header_name   = "Access-Control-Allow-Headers"
              value         = "Authorization, Content-Type, ocp-apim-subscription-key"
            },
            {
              header_action = "Overwrite"
              header_name   = "Access-Control-Allow-Methods"
              value         = "POST,PUT,GET,DELETE,OPTIONS"
            },
          ]
          url_rewrite_actions = [{
            source_pattern = "/"
            destination    = "/contoso"
          }]
        }
        conditions = {
          is_device_conditions = [{
            operator     = "Equal"
            match_values = ["Desktop"]
          }]
        }
      }]
    },
    {
      name                 = "my_rule_set2"
      custom_resource_name = "custom_rule2"
    },
  ]

  firewall_policies = [{
    name                              = "test"
    enabled                           = true
    mode                              = "Prevention"
    redirect_url                      = "https://www.contoso.com"
    custom_block_response_status_code = 403
    custom_block_response_body        = "PGh0bWw+CjxoZWFkZXI+PHRpdGxlPkhlbGxvPC90aXRsZT48L2hlYWRlcj4KPGJvZHk+CkhlbGxvIHdvcmxkCjwvYm9keT4KPC9odG1sPg=="

    custom_rules = [
      {
        name                           = "Rule1"
        enabled                        = true
        priority                       = 1
        rate_limit_duration_in_minutes = 1
        rate_limit_threshold           = 10
        type                           = "MatchRule"
        action                         = "Block"
        match_conditions = [{
          match_variable     = "RemoteAddr"
          operator           = "IPMatch"
          negation_condition = false
          match_values       = ["10.0.1.0/24", "10.0.0.0/24"]
        }]
      },
      {
        name                           = "Rule2"
        enabled                        = true
        priority                       = 2
        rate_limit_duration_in_minutes = 1
        rate_limit_threshold           = 10
        type                           = "MatchRule"
        action                         = "Block"
        match_conditions = [
          {
            match_variable     = "RemoteAddr"
            operator           = "IPMatch"
            negation_condition = false
            match_values       = ["192.168.1.0/24"]
          },
          {
            match_variable     = "RequestHeader"
            selector           = "UserAgent"
            operator           = "Contains"
            negation_condition = false
            match_values       = ["windows"]
            transforms         = ["Lowercase", "Trim"]
          },
        ]
      },
    ]

    managed_rules = [
      {
        type    = "DefaultRuleSet"
        version = "1.0"
        action  = "Log"
        exclusions = [{
          match_variable = "QueryStringArgNames"
          operator       = "Equals"
          selector       = "not_suspicious"
        }]
        overrides = [
          {
            rule_group_name = "PHP"
            rules = [{
              rule_id = "933100"
              enabled = false
              action  = "Block"
            }]
          },
          {
            rule_group_name = "SQLI"
            exclusions = [{
              match_variable = "QueryStringArgNames"
              operator       = "Equals"
              selector       = "really_not_suspicious"
            }]
            rules = [{
              rule_id = "942200"
              action  = "Block"
              exclusions = [{
                match_variable = "QueryStringArgNames"
                operator       = "Equals"
                selector       = "innocent"
              }]
            }]
          },
        ]
      },
      {
        type    = "Microsoft_BotManagerRuleSet"
        version = "1.0"
        action  = "Log"
      },
    ]
  }]

  security_policies = [{
    name                 = "MySecurityPolicy"
    custom_resource_name = "MyBetterNamedSecurityPolicy"
    firewall_policy_name = "test"
    patterns_to_match    = ["/*"]
    custom_domain_names  = ["www"]
    endpoint_names       = ["web", "azure"]
  }]

  extra_tags = {
    foo = "bar"
  }
}

Providers

Name Version
azurecaf ~> 1.2, >= 1.2.22
azurerm ~> 3.39

Modules

Name Source Version
diagnostics claranet/diagnostic-settings/azurerm ~> 7.0.0

Resources

Name Type
azurerm_cdn_frontdoor_custom_domain.cdn_frontdoor_custom_domain resource
azurerm_cdn_frontdoor_endpoint.cdn_frontdoor_endpoint resource
azurerm_cdn_frontdoor_firewall_policy.cdn_frontdoor_firewall_policy resource
azurerm_cdn_frontdoor_origin.cdn_frontdoor_origin resource
azurerm_cdn_frontdoor_origin_group.cdn_frontdoor_origin_group resource
azurerm_cdn_frontdoor_profile.cdn_frontdoor_profile resource
azurerm_cdn_frontdoor_route.cdn_frontdoor_route resource
azurerm_cdn_frontdoor_rule.cdn_frontdoor_rule resource
azurerm_cdn_frontdoor_rule_set.cdn_frontdoor_rule_set resource
azurerm_cdn_frontdoor_secret.cdn_frontdoor_secret resource
azurerm_cdn_frontdoor_security_policy.cdn_frontdoor_security_policy resource
azurecaf_name.cdn_frontdoor_custom_domain data source
azurecaf_name.cdn_frontdoor_endpoint data source
azurecaf_name.cdn_frontdoor_firewall_policy data source
azurecaf_name.cdn_frontdoor_origin data source
azurecaf_name.cdn_frontdoor_origin_group data source
azurecaf_name.cdn_frontdoor_profile data source
azurecaf_name.cdn_frontdoor_route data source
azurecaf_name.cdn_frontdoor_rule data source
azurecaf_name.cdn_frontdoor_rule_set data source
azurecaf_name.cdn_frontdoor_security_policy data source

Inputs

Name Description Type Default Required
cdn_frontdoor_profile_name Specifies the name of the FrontDoor Profile. string "" no
client_name Client name/account used in naming. string n/a yes
custom_diagnostic_settings_name Custom name of the diagnostics settings, name will be 'default' if not set. string "default" no
custom_domains CDN FrontDoor Custom Domains configurations.
list(object({
name = string
custom_resource_name = optional(string)
host_name = string
dns_zone_id = optional(string)
tls = optional(object({
certificate_type = optional(string, "ManagedCertificate")
minimum_tls_version = optional(string, "TLS12")
cdn_frontdoor_secret_id = optional(string)
key_vault_certificate_id = optional(string)
}), {})
}))
[] no
default_tags_enabled Option to enable or disable default tags. bool true no
endpoints CDN FrontDoor Endpoints configurations.
list(object({
name = string
prefix = optional(string)
custom_resource_name = optional(string)
enabled = optional(bool, true)
}))
[] no
environment Project environment. string n/a yes
extra_tags Extra tags to add. map(string) {} no
firewall_policies CDN Frontdoor Firewall Policies configurations.
list(object({
name = string
custom_resource_name = optional(string)
enabled = optional(bool, true)
mode = optional(string, "Prevention")
redirect_url = optional(string)
custom_block_response_status_code = optional(number)
custom_block_response_body = optional(string)
custom_rules = optional(list(object({
name = string
action = string
enabled = optional(bool, true)
priority = number
type = string
rate_limit_duration_in_minutes = optional(number, 1)
rate_limit_threshold = optional(number, 10)
match_conditions = list(object({
match_variable = string
match_values = list(string)
operator = string
selector = optional(string)
negate_condition = optional(bool)
transforms = optional(list(string), [])
}))
})), [])
managed_rules = optional(list(object({
type = string
version = optional(string, "1.0")
action = string
exclusions = optional(list(object({
match_variable = string
operator = string
selector = string
})), [])
overrides = optional(list(object({
rule_group_name = string
exclusions = optional(list(object({
match_variable = string
operator = string
selector = string
})), [])
rules = optional(list(object({
rule_id = string
action = string
enabled = optional(bool, true)
exclusions = optional(list(object({
match_variable = string
operator = string
selector = string
})), []) })), [])
})), [])
})), [])
}))
[] no
logs_categories Log categories to send to destinations. list(string) null no
logs_destinations_ids List of destination resources IDs for logs diagnostics destination. Can be Storage Account, Log Analytics Workspace and Event Hub. No more than one of each can be set. Empty list to disable logging. list(string) n/a yes
logs_metrics_categories Metrics categories to send to destinations. list(string) null no
name_prefix Optional prefix for the generated name string "" no
name_suffix Optional suffix for the generated name string "" no
origin_groups CDN FrontDoor Origin Groups configurations.
list(object({
name = string
custom_resource_name = optional(string)
session_affinity_enabled = optional(bool, true)
restore_traffic_time_to_healed_or_new_endpoint_in_minutes = optional(number, 10)
health_probe = optional(object({
interval_in_seconds = number
path = string
protocol = string
request_type = string
}))
load_balancing = optional(object({
additional_latency_in_milliseconds = optional(number, 50)
sample_size = optional(number, 4)
successful_samples_required = optional(number, 3)
}), {})
}))
[] no
origins CDN FrontDoor Origins configurations.
list(object({
name = string
custom_resource_name = optional(string)
origin_group_name = string
enabled = optional(bool, true)
certificate_name_check_enabled = optional(bool, true)

host_name = string
http_port = optional(number, 80)
https_port = optional(number, 443)
origin_host_header = optional(string)
priority = optional(number, 1)
weight = optional(number, 1)

private_link = optional(object({
request_message = optional(string)
target_type = optional(string)
location = string
private_link_target_id = string
}))
}))
[] no
resource_group_name Resource group name. string n/a yes
response_timeout_seconds Specifies the maximum response timeout in seconds. Possible values are between 16 and 240 seconds (inclusive). number 120 no
routes CDN FrontDoor Routes configurations.
list(object({
name = string
custom_resource_name = optional(string)
enabled = optional(bool, true)

endpoint_name = string
origin_group_name = string
origins_names = list(string)

forwarding_protocol = optional(string, "HttpsOnly")
patterns_to_match = optional(list(string), ["/*"])
supported_protocols = optional(list(string), ["Http", "Https"])
cache = optional(object({
query_string_caching_behavior = optional(string, "IgnoreQueryString")
query_strings = optional(list(string))
compression_enabled = optional(bool, false)
content_types_to_compress = optional(list(string))
}))

custom_domains_names = optional(list(string), [])
origin_path = optional(string, "/")
rule_sets_names = optional(list(string), [])

https_redirect_enabled = optional(bool, true)
link_to_default_domain = optional(bool, true)
}))
[] no
rule_sets CDN FrontDoor Rule Sets and associated Rules configurations.
list(object({
name = string
custom_resource_name = optional(string)
rules = optional(list(object({
name = string
custom_resource_name = optional(string)
order = number
behavior_on_match = optional(string, "Continue")

actions = object({
url_rewrite_actions = optional(list(object({
source_pattern = optional(string)
destination = optional(string)
preserve_unmatched_path = optional(bool, false)
})), [])
url_redirect_actions = optional(list(object({
redirect_type = string
destination_hostname = string
redirect_protocol = optional(string, "MatchRequest")
destination_path = optional(string, "")
query_string = optional(string, "")
destination_fragment = optional(string, "")
})), [])
route_configuration_override_actions = optional(list(object({
cache_duration = optional(string, "1.12:00:00")
cdn_frontdoor_origin_group_id = optional(string)
forwarding_protocol = optional(string, "MatchRequest")
query_string_caching_behavior = optional(string, "IgnoreQueryString")
query_string_parameters = optional(list(string))
compression_enabled = optional(bool, false)
cache_behavior = optional(string, "HonorOrigin")
})), [])
request_header_actions = optional(list(object({
header_action = string
header_name = string
value = optional(string)
})), [])
response_header_actions = optional(list(object({
header_action = string
header_name = string
value = optional(string)
})), [])
})

conditions = optional(object({
remote_address_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
})), [])
request_method_conditions = optional(list(object({
match_values = list(string)
operator = optional(string, "Equal")
negate_condition = optional(bool, false)
})), [])
query_string_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
post_args_conditions = optional(list(object({
post_args_name = string
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
request_uri_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
request_header_conditions = optional(list(object({
header_name = string
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
request_body_conditions = optional(list(object({
operator = string
match_values = list(string)
negate_condition = optional(bool, false)
transforms = optional(list(string), ["Lowercase"])
})), [])
request_scheme_conditions = optional(list(object({
operator = optional(string, "Equal")
negate_condition = optional(bool, false)
match_values = optional(string, "HTTP")
})), [])
url_path_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
url_file_extension_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = list(string)
transforms = optional(list(string), ["Lowercase"])
})), [])
url_filename_conditions = optional(list(object({
operator = string
match_values = list(string)
negate_condition = optional(bool, false)
transforms = optional(list(string), ["Lowercase"])
})), [])
http_version_conditions = optional(list(object({
match_values = list(string)
operator = optional(string, "Equal")
negate_condition = optional(bool, false)
})), [])
cookies_conditions = optional(list(object({
cookie_name = string
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
is_device_conditions = optional(list(object({
operator = optional(string, "Equal")
negate_condition = optional(bool, false)
match_values = optional(list(string), ["Mobile"])
})), [])
socket_address_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
})), [])
client_port_conditions = optional(list(object({
operator = string
negate_condition = optional(bool, false)
match_values = optional(list(string))
})), [])
server_port_conditions = optional(list(object({
operator = string
match_values = list(string)
negate_condition = optional(bool, false)
})), [])
host_name_conditions = optional(list(object({
operator = string
match_values = optional(list(string))
transforms = optional(list(string), ["Lowercase"])
})), [])
ssl_protocol_conditions = optional(list(object({
match_values = list(string)
operator = optional(string, "Equal")
negate_condition = optional(bool, false)
})), [])
}), null)
})), [])
}))
[] no
security_policies CDN FrontDoor Security policies configurations.
list(object({
name = string
custom_resource_name = optional(string)
firewall_policy_name = string
patterns_to_match = optional(list(string), ["/*"])
custom_domain_names = optional(list(string), [])
endpoint_names = optional(list(string), [])
}))
[] no
sku_name Specifies the SKU for this CDN FrontDoor Profile. Possible values include Standard_AzureFrontDoor and Premium_AzureFrontDoor. string "Standard_AzureFrontDoor" no
stack Project stack name. string n/a yes
use_caf_naming Use the Azure CAF naming provider to generate default resource name. custom_name override this if set. Legacy default name is used if this is set to false. bool true no

Outputs

Name Description
custom_domains CDN FrontDoor custom domains outputs.
endpoints CDN FrontDoor endpoints outputs.
firewall_policies CDN FrontDoor firewall policies outputs.
origin_groups CDN FrontDoor origin groups outputs.
origins CDN FrontDoor origins outputs.
profile_id The ID of the CDN FrontDoor Profile.
profile_name The name of the CDN FrontDoor Profile.
rule_sets CDN FrontDoor rule sets outputs.
rules CDN FrontDoor rules outputs.
security_policies CDN FrontDoor security policies outputs.

Related documentation

Azure Front Door REST API: docs.microsoft.com/en-us/rest/api/frontdoor/