### Install mapotf from Source Source: https://github.com/azure/mapotf/blob/main/README.md Installs the latest version of mapotf using Go. Ensure you have Go installed and configured. ```sh go install github.com/Azure/mapotf@latest ``` -------------------------------- ### Retrieve Provider Schema using Terraform Data Source: https://github.com/azure/mapotf/blob/main/doc/d/terraform.md This example demonstrates how to use the `data "terraform"` block to get provider source and version, and then use these attributes to fetch the schema for the `azurerm` provider. Ensure provider configurations and version constraints are correctly specified. ```hcl data "terraform" "version" { } data "provider_schema" "azurerm" { provider_source = data.terraform.version.required_providers["azurerm"].source provider_version = data.terraform.version.required_providers["azurerm"].version } ``` -------------------------------- ### Example Terraform ignore_changes Block Source: https://github.com/azure/mapotf/blob/main/README.md An example of an ignore_changes block within a lifecycle configuration, demonstrating how mapotf can dynamically add attributes to be ignored during Terraform state management. ```hcl lifecycle { ignore_changes = [ microsoft_defender[0].log_analytics_workspace_id, http_application_routing_enabled, http_proxy_config[0].no_proxy, kubernetes_version, public_network_access_enabled, # we might have a random suffix in cluster's name so we have to ignore it here, but we've traced user supplied cluster name by `null_resource.kubernetes_cluster_name_keeper` so when the name is changed we'll recreate this resource. name, ] ... ``` -------------------------------- ### Consolidate All Variables into variables.tf Source: https://github.com/azure/mapotf/blob/main/doc/t/move_block.md This example demonstrates moving all variable blocks into 'variables.tf' using a for_each loop. It preserves the relative order of variables within the destination file. ```terraform data "variable" "all" {} transform "move_block" "variables_to_variables_tf" { for_each = data.variable.all.result target_block_address = "variable.${each.key}" file_name = "variables.tf" } ``` -------------------------------- ### Detect Required vs Optional Variables Source: https://github.com/azure/mapotf/blob/main/doc/d/variable.md This example demonstrates how to differentiate between required and optional variables by checking for the presence of a 'default' attribute. It helps in identifying variables that must be provided. ```terraform data "variable" "all" {} locals { required_names = sort([for n, v in data.variable.all.result : n if !contains(keys(v), "default")]) optional_names = sort([for n, v in data.variable.all.result : n if contains(keys(v), "default")]) } ``` -------------------------------- ### Rename on a data source Source: https://github.com/azure/mapotf/blob/main/doc/t/rename_block_element.md This example demonstrates renaming an element within a data source. Use the `data.` prefix for `resource_type` to target data blocks. ```terraform transform "rename_block_element" "data_source_rename" { rename { resource_type = "data.azurerm_resources" element_path = ["resource_group"] new_name = "resource_group_name" } } ``` -------------------------------- ### Apply Consistent Input Ordering to All Module Calls Source: https://github.com/azure/mapotf/blob/main/doc/d/module_source.md This example demonstrates how to apply a consistent input ordering to all module calls within a repository. It combines `data "module"` and `data "module_source"` with `transform "reorder_attributes"` and `for_each` to enforce the contract uniformly. ```terraform data "module" "all" {} data "module_source" "naming" { source = "Azure/naming/azurerm" version = "~> 0.4" } # Look up the right module_source per module call by inspecting the # module block's `source` attribute. Here we keep it simple with a # single shared source. locals { module_addresses = { for name, _ in data.module.all.result : name => name } } transform "reorder_attributes" "module_inputs" { for_each = local.module_addresses target_block_address = "module.${each.key}" head_attributes = ["source", "version", "providers", "for_each", "count"] body_attributes = concat( data.module_source.naming.required_variables, data.module_source.naming.optional_variables, ) foot_attributes = ["depends_on"] } ``` -------------------------------- ### Head and foot together for resource attributes Source: https://github.com/azure/mapotf/blob/main/doc/t/reorder_attributes.md This example shows how to define both `head_attributes` and `foot_attributes` for a resource block. Attributes specified in `head_attributes` appear first, followed by the body attributes (sorted alphabetically by default), and then attributes in `foot_attributes` appear last. Blank lines separate these sections. ```terraform transform "reorder_attributes" "resource" { target_block_address = "resource.azurerm_storage_account.this" head_attributes = ["name", "resource_group_name", "location"] foot_attributes = ["tags"] } ``` -------------------------------- ### Look Up a Single Variable Source: https://github.com/azure/mapotf/blob/main/doc/d/variable.md This example shows how to use the 'name' argument to filter and retrieve a specific variable block. The result is a single entry, useful for targeted transformations. ```terraform data "variable" "location" { name = "location" } ``` -------------------------------- ### Sort Moved Blocks into moved.tf Source: https://github.com/azure/mapotf/blob/main/doc/d/moved.md This example shows how to use the 'sort_blocks_in_file' transform to consolidate all 'moved' blocks into a single 'moved.tf' file, maintaining a desired order. This pattern is used by AVM pre-commit for consistency. ```terraform data "moved" "all" {} locals { moved_addresses = sort([for idx, _ in data.moved.all.result : "moved.${idx}"]) } transform "sort_blocks_in_file" "moved_tf" { file_name = "moved.tf" desired_order = local.moved_addresses } ``` -------------------------------- ### Error Handling: Multiple Content Arguments Source: https://github.com/azure/mapotf/blob/main/doc/t/new_block.md This example demonstrates an error scenario where both `body` and `asraw` are set, which is not allowed. Only one of `asraw`, `asstring`, or `body` should be used to define the block content. ```terraform transform "new_block" example { new_block_type = "resource" filename = "main.tf" labels = ["azurerm_resource_group", "example"] body = <<-BODY name = "example" BODY asraw { location = "East US" } } ``` -------------------------------- ### Filtering Resource Blocks Using `count` Source: https://github.com/azure/mapotf/blob/main/doc/d/resource.md This example demonstrates how to filter resource blocks that specifically use the `count` attribute. Set `use_count` to `true` to enable this filtering. ```terraform data "resource" "example" { resource_type = "fake_resource" use_count = true } ``` -------------------------------- ### Retrieving Ephemeral Block Results Source: https://github.com/azure/mapotf/blob/main/doc/d/ephemeral.md This example shows how the results of a data "ephemeral" block are aggregated and exposed. The results are grouped by ephemeral type and then by block labels, with each block exposing its arguments as string-valued attributes. ```terraform ephemeral "azurerm_key_vault_secret" "example" { name = "db-password" key_vault_id = data.azurerm_key_vault.example.id } ``` ```terraform data "ephemeral" "example" { ephemeral_type = "azurerm_key_vault_secret" } ``` -------------------------------- ### Add Tags to Azure Kubernetes Cluster using `dynamic_block_body` Source: https://github.com/azure/mapotf/blob/main/doc/t/update_in_place.md This snippet shows an equivalent implementation to the `asstring` example, but using the `dynamic_block_body` attribute for adding auto-generated tags to Azure Kubernetes Cluster resources. It achieves the same result of merging new tags with existing ones. ```terraform data "resource" azurerm_kubernetes_cluster { resource_type = "azurerm_kubernetes_cluster" } transform "update_in_place" tracing_tags { for_each = try(data.resource.azurerm_kubernetes_cluster.result.azurerm_kubernetes_cluster, {}) target_block_address = each.value.mptf.block_address dynamic_block_body = <<-DYNAMIC_BODY tags = merge({ file = "${each.value.mptf.range.file_name}" block = "${each.value.mptf.terraform_address}" git_hash = "${each.value.mptf.module.git_hash}" module_source = "${each.value.mptf.module.source}" module_version = "${each.value.mptf.module.version}" }, ${try(each.value.tags, "{}")}) DYNAMIC_BODY ``` -------------------------------- ### Remove Multiple Blocks Using for_each Source: https://github.com/azure/mapotf/blob/main/doc/t/remove_block.md This example shows how to remove multiple blocks of a specific type based on a condition using `for_each`. It iterates over a map of legacy variables and removes each corresponding `variable` block. ```terraform data "variable" "all" {} locals { legacy_variables = { for name, v in data.variable.all.result : name => v if startswith(name, "legacy_") } } transform "remove_block" "drop_legacy_vars" { for_each = local.legacy_variables target_block_address = "variable.${each.key}" } ``` -------------------------------- ### Get provider schema Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This `provider_schema` data source retrieves the schema for a specified provider, using the provider's source and version defined in the Terraform configuration. ```hcl data "provider_schema" azurerm { provider_source = data.terraform.version.required_providers["azurerm"].source provider_version = data.terraform.version.required_providers["azurerm"].version } ``` -------------------------------- ### Remove Single Nested Block Source: https://github.com/azure/mapotf/blob/main/doc/t/remove_block_element.md This example demonstrates removing a single nested block from a resource. Ensure the target_block_address correctly points to the resource and the path accurately identifies the block to be removed. ```terraform transform "remove_block_element" this { target_block_address = "resource.fake_resource.this" paths = ["nested_block"] } ``` ```terraform resource "fake_resource" this { nested_block {} non_target_block {} } ``` -------------------------------- ### Preserve source order for the body Source: https://github.com/azure/mapotf/blob/main/doc/t/reorder_attributes.md This example uses `sort_body_alphabetically = false` to maintain the original order of attributes within the body of a block, instead of sorting them alphabetically. This is useful when the original order has semantic meaning or for preserving specific formatting. ```terraform transform "reorder_attributes" "preserve_order" { target_block_address = "resource.azurerm_storage_account.this" head_attributes = ["name", "resource_group_name", "location"] foot_attributes = ["tags"] sort_body_alphabetically = false } ``` -------------------------------- ### Remove Deeply Nested Block Source: https://github.com/azure/mapotf/blob/main/doc/t/remove_block_element.md This example shows how to remove a block that is nested multiple levels deep within a resource. The path should accurately reflect the hierarchy of the block to be removed. ```terraform transform "remove_block_element" this { target_block_address = "resource.fake_resource.this" paths = ["nested_block/second_nested_block"] } ``` ```terraform resource "fake_resource" this { nested_block { non_target_block {} } nested_block { second_nested_block {} } non_target_block {} } ``` -------------------------------- ### Drop Redundant "sensitive = false" Declarations Source: https://github.com/azure/mapotf/blob/main/doc/d/output.md This example demonstrates how to use the data "output" block in conjunction with a transform to remove default `sensitive = false` declarations from output blocks, simplifying your configuration. ```terraform data "output" "all" {} transform "remove_block_element" "drop_output_sensitive_false" { for_each = { for n, v in data.output.all.result : n => v if try(v.sensitive, true) == false } target_block_address = "output.${each.key}" paths = ["sensitive"] } ``` -------------------------------- ### Get block addresses Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This expression generates a list of block addresses for all Terraform resource blocks that support tags. ```hcl addresses = [for mptf in local.mptfs : mptf.block_address] ``` -------------------------------- ### Remove Attribute from Nested Block Source: https://github.com/azure/mapotf/blob/main/doc/t/remove_block_element.md Use this example to remove an attribute located within a nested block. The path should specify the nested block followed by the attribute name. ```terraform transform "remove_block_element" this { target_block_address = "resource.fake_resource.this" paths = ["nested_block/attr"] } ``` ```terraform resource "fake_resource" this { attr = 1 nested_block { attr = "hello" } } ``` -------------------------------- ### Running MapoTF Transformation Source: https://github.com/azure/mapotf/blob/main/tutorial/1.MapoTF_DSL_Basics.md Executes a MapoTF transformation using the CLI, specifying the directories for Terraform and MapoTF configurations. ```shell mapotf transform --tf-dir ./terraform --mptf-dir ./mapotf ``` -------------------------------- ### Schema-driven body ordering with reorder_attributes Source: https://github.com/azure/mapotf/blob/main/doc/d/provider_schema.md Demonstrates how to use the provider schema to enforce a consistent attribute order (required, optional, then others) for resource bodies. This ensures uniformity across resources of the same type. ```terraform data "provider_schema" "azurerm" { provider_source = "hashicorp/azurerm" provider_version = "~> 4.0" } data "resource" "all" {} locals { resource_addresses = merge([ for t, rs in data.resource.all.result : { for n, _ in rs : "resource.${t}.${n}" => t } ]...) } transform "reorder_attributes" "resource_body" { for_each = local.resource_addresses target_block_address = each.key head_attributes = ["for_each", "count", "provider"] body_attributes = concat( try(data.provider_schema.azurerm.resources_required_attributes[each.value], []), try(data.provider_schema.azurerm.resources_optional_attributes[each.value], []), ) foot_attributes = ["lifecycle", "depends_on"] } ``` -------------------------------- ### Sort Every Output Alphabetically into outputs.tf Source: https://github.com/azure/mapotf/blob/main/doc/d/output.md This snippet shows the canonical AVM pre-commit rule for outputs. It uses the data "output" block and a transform to ensure all output blocks are collected and sorted alphabetically into the `outputs.tf` file. ```terraform data "output" "all" {} locals { output_addresses = sort([for name, _ in data.output.all.result : "output.${name}"]) } transform "sort_blocks_in_file" "outputs_tf" { file_name = "outputs.tf" desired_order = local.output_addresses } ``` -------------------------------- ### Original Terraform Configuration Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This is the initial Terraform configuration before applying `mapotf` transformations. It shows resources like `azurerm_resource_group` and `azurerm_storage_account` with and without existing tags. ```hcl terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "3.112.0" } } } resource "azurerm_resource_group" "this" { location = "West US" name = "example-resources" } resource "azurerm_storage_account" "this" { name = "storageaccountname" resource_group_name = azurerm_resource_group.this.name location = azurerm_resource_group.this.location account_tier = "Standard" account_replication_type = "LRS" tags = { env = "prod" } } resource "azurerm_subnet" "this" { address_prefixes = [] name = "" resource_group_name = "" virtual_network_name = "" } ``` -------------------------------- ### Remove Attribute from Dynamic Nested Block Source: https://github.com/azure/mapotf/blob/main/doc/t/remove_block_element.md This example shows how to remove an attribute from a dynamic nested block. The path specifies the dynamic block name and the attribute within its content. ```terraform transform "remove_block_element" this { target_block_address = "resource.fake_resource.this" paths = ["nested_block/attr"] } ``` ```terraform resource "fake_resource" this { attr = 1 dynamic "nested_block" { for_each = [1] content { attr = "hello" } } } ``` -------------------------------- ### Fetch All Resources Source: https://github.com/azure/mapotf/blob/main/example/prevent_destroy/readme.md A data block to fetch all resources in the Terraform configuration, regardless of their type. ```terraform data "resource" all_resource { } ``` -------------------------------- ### Applying Transformations to Resources Source: https://github.com/azure/mapotf/blob/main/tutorial/1.MapoTF_DSL_Basics.md Applies a transformation to add metadata tags to AKS clusters by iterating over queried resources and updating their tags. ```hcl transform "update_in_place" "add_tracing_tags" { for_each = data.resource.aks_clusters.result target_block_address = each.value.mptf.block_address asstring { tags = <<-TAGS merge({ file = "${each.value.mptf.range.file_name}" block = "${each.value.mptf.terraform_address}" git_hash = "${each.value.mptf.module.git_hash}" module_source = "${each.value.mptf.module.source}" module_version = "${each.value.mptf.module.version}" }, ${try(each.value.tags, "{}")}) TAGS } } ``` -------------------------------- ### Define New Block using asstring Source: https://github.com/azure/mapotf/blob/main/doc/t/new_block.md Use this snippet to define the body of a new resource block using a string expression. The expression is evaluated and inserted as tokens into the Terraform configuration. ```terraform transform "new_block" example { new_block_type = "resource" filename = "main.tf" labels = ["azurerm_resource_group", "example"] asstring { name = var.resource_group_name location = "East US" } } ``` -------------------------------- ### Terraform Configuration Before Transformation Source: https://github.com/azure/mapotf/blob/main/example/prevent_destroy/readme.md This is the initial Terraform configuration for creating a resource group and a virtual network with subnets before applying any mapotf transformations. ```terraform resource "random_id" "rg_name" { byte_length = 8 } resource "azurerm_resource_group" "example" { location = var.location name = "azure-subnets-${random_id.rg_name.hex}-rg" } locals { subnets = { for i in range(3) : "subnet${i}" => { address_prefixes = [cidrsubnet(local.virtual_network_address_space, 8, i)] } } virtual_network_address_space = "10.0.0.0/16" } module "vnet" { source = "Azure/subnets/azurerm" version = "1.0.0" resource_group_name = azurerm_resource_group.example.name subnets = local.subnets virtual_network_address_space = [local.virtual_network_address_space] virtual_network_location = var.vnet_location virtual_network_name = "azure-subnets-vnet" } ``` -------------------------------- ### Prepare Resource Addresses Source: https://github.com/azure/mapotf/blob/main/example/prevent_destroy/readme.md Prepares a list of block addresses for resources, optionally filtering to only include those in the root module based on the root_only variable. ```terraform locals { all_resource_blocks = flatten([ for resource_type, resource_blocks in data.resource.all_resource.result : resource_blocks ]) mptfs = flatten([for _, blocks in local.all_resource_blocks : [for b in blocks : b.mptf]]) addresses = var.root_only ? [for mptf in local.mptfs : mptf.block_address if mptf.module.dir == "."] : [for mptf in local.mptfs : mptf.block_address] } ``` -------------------------------- ### Move Every Module Block into main.tf Source: https://github.com/azure/mapotf/blob/main/doc/d/module.md This common composition pattern moves all module blocks to the `main.tf` file. It's frequently used to enforce project structure, ensuring modules are consistently located. ```terraform data "module" "all" {} transform "move_block" "modules_to_main" { for_each = data.module.all.result target_block_address = "module.${each.key}" file_name = "main.tf" } ``` -------------------------------- ### Basic Terraform Data Block Source: https://github.com/azure/mapotf/blob/main/doc/d/terraform.md This snippet shows the basic syntax for declaring a `data "terraform"` block. Use this to retrieve general Terraform configuration information. ```hcl data "terraform" "example" { } ``` -------------------------------- ### Put type and description first on every variable Source: https://github.com/azure/mapotf/blob/main/doc/t/reorder_attributes.md This snippet demonstrates how to use `head_attributes` to ensure that the `type` and `description` attributes appear first in all variable blocks. It uses a `data.variable.all` to iterate over all defined variables. ```terraform data "variable" "all" {} transform "reorder_attributes" "variables" { for_each = data.variable.all.result target_block_address = "variable.${each.key}" head_attributes = ["type", "description"] } ``` -------------------------------- ### Enumerate All Variable Blocks Source: https://github.com/azure/mapotf/blob/main/doc/d/variable.md This snippet shows how to enumerate all variable blocks in the configuration and generate a list of their addresses. It's useful for understanding the scope of variables defined. ```terraform data "variable" "all" {} locals { variable_addresses = [for name, _ in data.variable.all.result : "variable.${name}"] } ``` -------------------------------- ### Schema-driven body ordering with body_attributes Source: https://github.com/azure/mapotf/blob/main/doc/t/reorder_attributes.md This snippet demonstrates using `body_attributes` in conjunction with `data.provider_schema` to order the body of a resource block based on provider schema definitions. It first lists required attributes, then optional attributes, ensuring a structured and schema-compliant body. ```terraform data "provider_schema" "azurerm" { provider_source = "hashicorp/azurerm" provider_version = "~> 4.0" } transform "reorder_attributes" "resource_group_body" { target_block_address = "resource.azurerm_resource_group.this" head_attributes = ["for_each", "count", "provider"] body_attributes = concat( try(data.provider_schema.azurerm.resources_required_attributes["azurerm_resource_group"], []), try(data.provider_schema.azurerm.resources_optional_attributes["azurerm_resource_group"], []), ) foot_attributes = ["lifecycle", "depends_on"] } ``` -------------------------------- ### Enumerate All Moved Blocks Source: https://github.com/azure/mapotf/blob/main/doc/d/moved.md This snippet demonstrates how to enumerate all 'moved' blocks using the data source and create a sorted list of their addresses. Use this to dynamically reference moved blocks. ```terraform data "moved" "all" {} locals { moved_addresses = sort([for idx, _ in data.moved.all.result : "moved.${idx}"]) } ``` -------------------------------- ### Map block addresses to resources Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This expression creates a map where keys are the block addresses of all resource blocks and values are the resource blocks themselves. ```hcl all_resources = { for obj in flatten([for obj in flatten([for b in data.resource.all.result.* : [for nb in b : nb]]) : [for body in obj : body]]) : obj.mptf.block_address=>obj} ``` -------------------------------- ### Create New Resource Block with asraw Source: https://github.com/azure/mapotf/blob/main/doc/t/new_block.md Use this snippet to create a new resource block dynamically. It requires specifying the block type, filename, labels, and the raw HCL content for the block. ```terraform transform "new_block" example { new_block_type = "resource" filename = "main.tf" labels = ["azurerm_resource_group", "rg"] asraw { name = "example" location = "East US" } } ``` -------------------------------- ### Enumerate Every Output Block Source: https://github.com/azure/mapotf/blob/main/doc/d/output.md Use this snippet to retrieve all output blocks defined in your Terraform configuration. It's useful for generating lists of output addresses or for further processing. ```terraform data "output" "all" {} locals { output_addresses = sort([for name, _ in data.output.all.result : "output.${name}"]) } ``` -------------------------------- ### Terraform Configuration After Applying tracing_tags Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This configuration shows the result after `mapotf transform` is applied. Default tracing tags have been added to resources that support them, like `azurerm_resource_group` and `azurerm_storage_account`, while `azurerm_subnet` remains unchanged as it does not support tags. Existing tags on `azurerm_storage_account` are preserved using the `merge` function. ```hcl terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "3.112.0" } } } resource "azurerm_resource_group" "this" { location = "West US" name = "example-resources" tags = var.tracing_tags_enabled ? { file = "main.tf" block = "azurerm_resource_group.this" module_source = try(one(data.modtm_module_source.telemetry).module_source, "") module_version = try(one(data.modtm_module_source.telemetry).module_version, "") } : {} } resource "azurerm_storage_account" "this" { name = "storageaccountname" resource_group_name = azurerm_resource_group.this.name location = azurerm_resource_group.this.location account_tier = "Standard" account_replication_type = "LRS" tags = merge({ env = "prod" }, var.tracing_tags_enabled ? { file = "main.tf" block = "azurerm_storage_account.this" module_source = try(one(data.modtm_module_source.telemetry).module_source, "") module_version = try(one(data.modtm_module_source.telemetry).module_version, "") } : {}) } resource "azurerm_subnet" "this" { address_prefixes = [] name = "" resource_group_name = "" virtual_network_name = "" } ``` -------------------------------- ### Sort Variables Alphabetically into variables.tf Source: https://github.com/azure/mapotf/blob/main/doc/t/sort_blocks_in_file.md This snippet demonstrates how to use the `sort_blocks_in_file` transform to collect all `variable` blocks and place them alphabetically into a `variables.tf` file. It relies on a `data "variable" "all"` source to enumerate existing variables. ```terraform data "variable" "all" {} locals { variable_addresses = sort([for name, _ in data.variable.all.result : "variable.${name}"]) } transform "sort_blocks_in_file" "variables_tf" { file_name = "variables.tf" desired_order = local.variable_addresses } ``` -------------------------------- ### Query Data Blocks by Type Source: https://github.com/azure/mapotf/blob/main/doc/d/data.md Use this snippet to query all data blocks of a specific type, such as 'azurerm_client_config'. ```terraform data "data" "example" { data_source_type = "azurerm_client_config" } ``` -------------------------------- ### Querying Resource Blocks by Type Source: https://github.com/azure/mapotf/blob/main/doc/d/resource.md Use this snippet to query all resource blocks of a specific type, such as `azurerm_kubernetes_cluster`. The results are stored in the data source. ```terraform data "resource" "aks" { resource_type = "azurerm_kubernetes_cluster" } ``` -------------------------------- ### Add Tags to Azure Kubernetes Cluster using `asstring` Source: https://github.com/azure/mapotf/blob/main/doc/t/update_in_place.md This snippet demonstrates how to add auto-generated tags to Azure Kubernetes Cluster resources using the `asstring` attribute within the `update_in_place` transform block. It merges new tags with existing ones, including file name, block address, and module information. ```terraform data "resource" azurerm_kubernetes_cluster { resource_type = "azurerm_kubernetes_cluster" } transform "update_in_place" tracing_tags { for_each = try(data.resource.azurerm_kubernetes_cluster.result.azurerm_kubernetes_cluster, {}) target_block_address = each.value.mptf.block_address asstring { tags = <<-TAGS merge({ file = "${each.value.mptf.range.file_name}" block = "${each.value.mptf.terraform_address}" git_hash = "${each.value.mptf.module.git_hash}" module_source = "${each.value.mptf.module.source}" module_version = "${each.value.mptf.module.version}" }, ${try(each.value.tags, "{}")}) TAGS } } ``` -------------------------------- ### Enumerate Every Module Block Source: https://github.com/azure/mapotf/blob/main/doc/d/module.md Use this snippet to retrieve all module blocks in your Terraform configuration. It's useful for generating lists of module addresses or for applying transformations to all modules. ```terraform data "module" "all" {} locals { module_addresses = [for name, _ in data.module.all.result : "module.${name}"] } ``` -------------------------------- ### Running MapoTF Transformation Recursively Source: https://github.com/azure/mapotf/blob/main/tutorial/1.MapoTF_DSL_Basics.md Applies transformations recursively to all Terraform modules within the specified directory using the `--recursive` flag. ```shell mapotf transform --tf-dir ./terraform --mptf-dir ./mapotf --recursive ``` -------------------------------- ### Querying Terraform Resources Source: https://github.com/azure/mapotf/blob/main/tutorial/1.MapoTF_DSL_Basics.md Uses a data block to fetch information about specific Terraform resources, such as Azure Kubernetes Service clusters. ```hcl data "resource" "aks_clusters" { resource_type = "azurerm_kubernetes_cluster" } ``` -------------------------------- ### AKS Module `ignore_changes` After Transformation Source: https://github.com/azure/mapotf/blob/main/example/customize_aks_ignore_changes/readme.md Illustrates the `ignore_changes` configuration after mapotf has been applied. It includes the `microsoft_defender[0].log_analytics_workspace_id` attribute, demonstrating how mapotf can automatically update lifecycle settings. ```terraform ignore_changes = [ microsoft_defender[0].log_analytics_workspace_id, http_application_routing_enabled, http_proxy_config[0].no_proxy, kubernetes_version, public_network_access_enabled, # we might have a random suffix in cluster's name so we have to ignore it here, but we've traced user supplied cluster name by `null_resource.kubernetes_cluster_name_keeper` so when the name is changed we'll recreate this resource. name, ] ``` -------------------------------- ### Match resources with tags Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This expression selects all `resource` blocks from the current Terraform configuration that match the resource types identified as supporting tags. ```hcl resource_support_tags = flatten([ for resource_type, resource_blocks in data.resource.all.result : resource_blocks if contains(local.resources_support_tags, resource_type) ]) ``` -------------------------------- ### Update resource tags in place Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This transform applies updates to the `tags` attribute of resources. If tracing tags are enabled, it merges existing tags with new tracing information including file, block, module source, and version. ```hcl transform "update_in_place" tags { for_each = try(local.addresses, []) target_block_address = each.value asstring { tags = <<-TAGS %{if try(local.all_resources[each.value].tags != "", false)}merge(${local.all_resources[each.value].tags}, var.tracing_tags_enabled ? { file = "${local.all_resources[each.value].mptf.range.file_name}" block = "${local.all_resources[each.value].mptf.terraform_address}" module_source = try(one(data.modtm_module_source.telemetry).module_source, "") module_version = try(one(data.modtm_module_source.telemetry).module_version, "") } : {}) %{else} var.tracing_tags_enabled ? { file = "${local.all_resources[each.value].mptf.range.file_name}" ``` -------------------------------- ### Terraform For Each Loop Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This snippet defines the 'for_each' argument for a Terraform resource, iterating over a local variable named 'addresses'. ```hcl for_each = try(local.addresses, []) ``` -------------------------------- ### Nested block in head_attributes Source: https://github.com/azure/mapotf/blob/main/doc/t/reorder_attributes.md This snippet illustrates placing a nested block, such as `network_interface`, at the beginning of a resource block by including it in `head_attributes`. Similarly, other nested blocks like `lifecycle` can be moved to the `foot_attributes`. ```terraform transform "reorder_attributes" "vm" { target_block_address = "resource.azurerm_virtual_machine.this" head_attributes = ["count", "for_each", "network_interface"] foot_attributes = ["lifecycle", "depends_on"] } ``` -------------------------------- ### Retrieve AzureRM Provider Schema Source: https://github.com/azure/mapotf/blob/main/doc/d/provider_schema.md Fetches the schema for the AzureRM provider, specifying the source and version constraint. This data can then be used to analyze provider schema details. ```hcl data "provider_schema" azurerm { provider_source = "hashicorp/azurerm" provider_version = "~> 4.0" } locals { resources_support_tags = toset([for name, r in data.provider_schema.azurerm.resources : name if try(r.block.attributes["tags"].type == ["map", "string"], false)]) } ``` -------------------------------- ### Retrieve all resource blocks Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This HCL data source block retrieves all `resource` blocks within the current Terraform configuration. ```hcl data "resource" all { } ``` -------------------------------- ### Local Variable Before Regex Replace Source: https://github.com/azure/mapotf/blob/main/doc/t/regex_replace_expression.md This code shows a local variable definition before the `regex_replace_expression` transform is applied, illustrating the initial state of an expression that will be modified. ```terraform locals { azurerm_kubernetes_cluster_location = azurerm_kubernetes_cluster.example[0].location } ``` -------------------------------- ### Look Up a Specific Module Block Source: https://github.com/azure/mapotf/blob/main/doc/d/module.md Use this snippet to find a single module block by its label. This is helpful for targeting specific modules for operations like `for_each` filters or block transformations. ```terraform data "module" "naming" { name = "naming" } ``` -------------------------------- ### Terraform Resource Block with Tracing Tags Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This snippet shows how to dynamically generate tracing tags for a Terraform resource block. It merges existing tags with generated tracing tags if tracing is enabled, otherwise it only applies generated tags. ```hcl tags = <<-TAGS %{if try(local.all_resources[each.value].tags != "", false)}merge(${local.all_resources[each.value].tags}, var.tracing_tags_enabled ? { file = "${local.all_resources[each.value].mptf.range.file_name}" block = "${local.all_resources[each.value].mptf.terraform_address}" module_source = try(one(data.modtm_module_source.telemetry).module_source, "") module_version = try(one(data.modtm_module_source.telemetry).module_version, "") } : {}) %{else} var.tracing_tags_enabled ? { file = "${local.all_resources[each.value].mptf.range.file_name}" block = "${local.all_resources[each.value].mptf.terraform_address}" module_source = try(one(data.modtm_module_source.telemetry).module_source, "") module_version = try(one(data.modtm_module_source.telemetry).module_version, "") } : {}%{ endif} TAGS ``` -------------------------------- ### Accessing Query Results with Mapotf Expressions Source: https://github.com/azure/mapotf/blob/main/doc/d/resource.md These Mapotf expressions help process the results from a `data "resource"` query. `flatten` is used to create a list of resource blocks, and a map is created for easier access by block address. ```hcl locals { kubernetes_cluster_resource_blocks = flatten([for _, blocks in flatten(data.resource.all.result) : [for b in blocks : b]]) kubernetes_cluster_resource_blocks_map = { for block in local.kubernetes_cluster_resource_blocks : block.mptf.block_address => block } } ``` -------------------------------- ### Order Module Inputs by Required then Optional Source: https://github.com/azure/mapotf/blob/main/doc/d/module_source.md Use this snippet to reorder the attributes of a module block. It ensures that required variables are listed first, followed by optional variables, matching the source module's contract. This is useful for standardizing module call sites. ```terraform data "module_source" "naming" { source = "Azure/naming/azurerm" version = "~> 0.4" } transform "reorder_attributes" "naming_module" { target_block_address = "module.naming" head_attributes = ["source", "version", "providers", "for_each", "count"] body_attributes = concat( data.module_source.naming.required_variables, data.module_source.naming.optional_variables, ) foot_attributes = ["depends_on"] } ``` -------------------------------- ### Transform mapotf Configuration without Applying Source: https://github.com/azure/mapotf/blob/main/README.md Applies mapotf transformations to Terraform files without immediately running 'terraform apply'. This allows for review of the transformed files and backup files before committing changes. ```sh mapotf transform -r --mptf-dir git::https://github.com/Azure/mapotf.git//example/customize_aks_ignore_changes ``` -------------------------------- ### Ensure Local Value Exists (New Block) Source: https://github.com/azure/mapotf/blob/main/doc/t/ensure_local.md Use this snippet to ensure a local value is defined. If it doesn't exist, a new `locals` block will be created in the specified `fallback_file_name`. This is useful for injecting default values or required metadata. ```terraform transform "ensure_local" "telemetry_id" { name = "telemetry_id" fallback_file_name = "main.tf" value_as_string = "\"00000000-0000-0000-0000-000000000000\"" } ``` -------------------------------- ### Create Private Endpoint Resource Block Source: https://github.com/azure/mapotf/blob/main/example/new_private_endpoint_for_cognitive_account/readme.md This `transform` block defines how to create an `azurerm_private_endpoint` resource. It iterates over the fetched cognitive account resources and sets essential properties like `location` and `resource_group_name` based on the source resource's attributes. ```terraform transform "new_block" "private_endpoints_resource" { for_each = try(data.resource.cognitive_account.result.azurerm_cognitive_account, {}) filename = "main.tf" new_block_type = "resource" labels = ["azurerm_private_endpoint", "this"] asstring { location = "${each.value.mptf.terraform_address}.location" resource_group_name = "coalesce(each.value.resource_group_name, ${each.value.mptf.terraform_address}.resource_group_name)" ``` -------------------------------- ### Retrieve Terraform block Source: https://github.com/azure/mapotf/blob/main/example/tracing_tags/readme.md This HCL data source block retrieves the `terraform` block from the current Terraform configuration. ```hcl data "terraform" version { } ``` -------------------------------- ### Enumerate all locals Source: https://github.com/azure/mapotf/blob/main/doc/d/local.md Use this snippet to retrieve a map of all local values defined in your Terraform configuration. The result is keyed by local name and includes the stringified HCL expression for each local. ```terraform data "local" "all" {} locals { local_names = sort(keys(data.local.all.result)) } ``` -------------------------------- ### Update Prevent Destroy using asstring Source: https://github.com/azure/mapotf/blob/main/example/prevent_destroy/readme.md Applies the prevent_destroy lifecycle setting to resources using the update_in_place transform and the asstring attribute. This method automatically converts the boolean variable to its string representation. ```terraform transform "update_in_place" set_prevent_destroy { for_each = try(local.addresses, []) target_block_address = each.value asstring { lifecycle { prevent_destroy = var.prevent_destroy } } } ``` -------------------------------- ### Fetch AKS Cluster Resources Source: https://github.com/azure/mapotf/blob/main/example/customize_aks_ignore_changes/readme.md Use the `data "resource" aks` block to retrieve all `azurerm_kubernetes_cluster` resources within your Terraform configuration. ```terraform data "resource" aks { resource_type = "azurerm_kubernetes_cluster" } ``` -------------------------------- ### Filtering Resource Blocks Using `for_each` Source: https://github.com/azure/mapotf/blob/main/doc/d/resource.md Use this snippet to filter resource blocks that are configured with the `for_each` attribute. Set `use_for_each` to `true` to apply this filter. ```terraform data "resource" "example" { resource_type = "fake_resource" use_for_each = true } ``` -------------------------------- ### Apply mapotf Transformation to AKS Cluster Source: https://github.com/azure/mapotf/blob/main/README.md Applies a specific mapotf transformation to add 'microsoft_defender[0].log_analytics_workspace_id' to the ignore_changes list for azurerm_kubernetes_cluster resources. This command also triggers a Terraform apply. ```sh mapotf apply -r --mptf-dir git::https://github.com/Azure/mapotf.git//example/customize_aks_ignore_changes ``` -------------------------------- ### Inject Multi-line Expression with value_as_string Source: https://github.com/azure/mapotf/blob/main/doc/t/ensure_local.md Use `value_as_string` to inject complex or multi-line expressions that are easier to represent as a string. The string is parsed as an HCL expression at apply time, resulting in the unquoted expression in the rendered file. This is useful for dynamically constructing complex values like merged maps. ```terraform transform "ensure_local" "tags" { name = "tags" fallback_file_name = "main.tf" value_as_string = "merge(var.tags, { managed_by = \"terraform\" })" } ``` -------------------------------- ### Check if a specific local is defined Source: https://github.com/azure/mapotf/blob/main/doc/d/local.md This pattern demonstrates how to conditionally apply a transform only if a specific local value is not already defined elsewhere in your configuration. It uses the `data "local"` source to check for the existence of a local by name. ```terraform data "local" "telemetry_id" { name = "telemetry_id" } transform "ensure_local" "inject_telemetry" { # Only ensure if not already defined elsewhere. count = contains(keys(data.local.telemetry_id.result), "telemetry_id") ? 0 : 1 name = "telemetry_id" fallback_file_name = "main.tf" value_as_string = "\"00000000-0000-0000-0000-000000000000\"" } ``` -------------------------------- ### Disable section blanks Source: https://github.com/azure/mapotf/blob/main/doc/t/reorder_attributes.md By setting `head_foot_line_breaks` to `false`, this transform ensures that the head, body, and foot sections of a configuration block are emitted contiguously without any blank lines separating them. Nested blocks still maintain their standard blank line separation. ```terraform transform "reorder_attributes" "module" { target_block_address = "module.example" head_attributes = ["source", "version"] foot_attributes = ["depends_on"] head_foot_line_breaks = false } ``` -------------------------------- ### Create New Variable Block with body Source: https://github.com/azure/mapotf/blob/main/doc/t/new_block.md Use this snippet to create a new variable block. It requires specifying the block type, filename, labels, and the HCL content for the variable's body. ```terraform transform "new_block" example { new_block_type = "variable" filename = "variables.tf" labels = ["example"] body = <<-BODY type = string description = "This is an example variable" BODY } ```