├── avm.bat ├── tests ├── unit │ └── unit.go └── README.md ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── avm_question_feedback.yml │ └── avm_module_issue.yml ├── dependabot.yml ├── workflows │ └── pr-check.yml ├── actions │ ├── linting │ │ └── action.yml │ ├── e2e-getexamples │ │ └── action.yml │ ├── e2e-testexamples │ │ └── action.yml │ └── docs-check │ │ └── action.yml ├── PULL_REQUEST_TEMPLATE.md ├── policies │ ├── eventResponder.yml │ └── scheduledSearches.yml └── copilot-instructions.md ├── examples ├── default │ ├── _header.md │ ├── locals.tf │ ├── variables.tf │ ├── _footer.md │ ├── main.tf │ └── README.md ├── deploy_fw_policy_with_rule_collection_group │ ├── _header.md │ ├── locals.tf │ ├── variables.tf │ ├── _footer.md │ ├── main.tf │ └── README.md ├── deploy_fw_policy_for_avd │ ├── _header.md │ ├── locals.tf │ ├── variables.tf │ ├── _footer.md │ ├── main.tf │ └── README.md ├── deploy_fw_policy_with_ipgroups │ ├── _header.md │ ├── locals.tf │ ├── variables.tf │ ├── _footer.md │ ├── main.tf │ └── README.md ├── README.md └── .terraform-docs.yml ├── locals.tf ├── .vscode ├── extensions.json ├── settings.json └── mcp.json ├── modules ├── README.md ├── rule_collection_groups │ ├── terraform.tf │ ├── outputs.tf │ ├── _header.md │ ├── _footer.md │ ├── main.tf │ ├── variables.tf │ └── README.md └── .terraform-docs.yml ├── .editorconfig ├── Makefile ├── _header.md ├── CODE_OF_CONDUCT.md ├── terraform.tf ├── outputs.tf ├── .devcontainer └── devcontainer.json ├── _footer.md ├── CONTRIBUTING.md ├── SUPPORT.md ├── LICENSE ├── .terraform-docs.yml ├── .gitignore ├── main.telemetry.tf ├── avmmakefile ├── SECURITY.md ├── avm ├── avm.ps1 ├── main.tf ├── AGENTS.md ├── variables.tf └── README.md /avm.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | .\avm.ps1 %1 3 | -------------------------------------------------------------------------------- /tests/unit/unit.go: -------------------------------------------------------------------------------- 1 | package unit 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.csv text eol=crlf 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | .github/CODEOWNERS @Azure/avm-core-team-technical-terraform 2 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Create tests in the provided subdirectories. 4 | -------------------------------------------------------------------------------- /examples/default/_header.md: -------------------------------------------------------------------------------- 1 | # Default example 2 | 3 | This deploys the module in its simplest form. 4 | -------------------------------------------------------------------------------- /locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | role_definition_resource_substring = "providers/Microsoft.Authorization/roleDefinitions" 3 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "EditorConfig.EditorConfig", 4 | "hashicorp.terraform", 5 | "ms-azurertools.vscode-azureterraform" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | # Sub-modules 2 | 3 | Create directories for each sub-module if required. 4 | README.md files will be automatically generated for each sub-module using `terraform-docs`. 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.eol": "\n", 3 | "chat.agent.maxRequests": 500, 4 | "chat.math.enabled": true, 5 | "chat.todoListTool.enabled": true, 6 | "github.copilot.chat.agent.thinkingTool": true 7 | } 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: New AVM Module Proposal 📝 4 | url: https://aka.ms/AVM/ModuleProposal 5 | about: Want a new AVM Module to exist? Let us know! -------------------------------------------------------------------------------- /modules/rule_collection_groups/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = ">= 3.71, < 5.0.0" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | AVM_MAKEFILE_REF := main 3 | 4 | $(shell curl -H 'Cache-Control: no-cache, no-store' -sSL "https://raw.githubusercontent.com/Azure/avm-terraform-governance/$(AVM_MAKEFILE_REF)/Makefile" -o avmmakefile) 5 | -include avmmakefile 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | 4 | updates: 5 | - package-ecosystem: gomod 6 | directory: /tests 7 | schedule: 8 | interval: daily 9 | - package-ecosystem: "github-actions" 10 | directory: "/.github/workflows" 11 | schedule: 12 | interval: daily 13 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_rule_collection_group/_header.md: -------------------------------------------------------------------------------- 1 | # Deploy Azure Firewall Policy with Rule Collection Groups 2 | 3 | This example deploys Azure Firewall Policy with Rules Collection Groups 4 | 5 | - Firewall Policy 6 | - Rule Collection Groups 7 | - Rule Collections 8 | - Network and Application Rules 9 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_for_avd/_header.md: -------------------------------------------------------------------------------- 1 | # Deploy Firewall Policy for Azure Virtual Desktop 2 | 3 | This example deploys an Azure Firewall Policy with the required rules needed for Azure Virtual Desktop. 4 | 5 | - Firewall Policy 6 | - Rule Collection Groups 7 | - Rule Collections 8 | - Network and Application Rules 9 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_ipgroups/_header.md: -------------------------------------------------------------------------------- 1 | # Deploy Firewall Policy with IP Groups 2 | 3 | This deploys an Azure Firewall Policy with IP Groups in the rules 4 | 5 | - Firewall 6 | - Firewall Policy 7 | - Rule Collection Groups 8 | - Rule Collections 9 | - Network and Application Rules 10 | - IP Groups 11 | -------------------------------------------------------------------------------- /examples/default/locals.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | azure_regions = [ 4 | "westeurope", 5 | "northeurope", 6 | "eastus", 7 | "eastus2", 8 | "westus", 9 | "westus2", 10 | "southcentralus", 11 | "northcentralus", 12 | "centralus", 13 | "eastasia", 14 | "southeastasia", 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_for_avd/locals.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | azure_regions = [ 4 | "westeurope", 5 | "northeurope", 6 | "eastus", 7 | "eastus2", 8 | "westus", 9 | "westus2", 10 | "southcentralus", 11 | "northcentralus", 12 | "centralus", 13 | "eastasia", 14 | "southeastasia", 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_ipgroups/locals.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | azure_regions = [ 4 | "westeurope", 5 | "northeurope", 6 | "eastus", 7 | "eastus2", 8 | "westus3", 9 | "westus2", 10 | "southcentralus", 11 | "canadacentral", 12 | "centralus", 13 | "eastasia", 14 | "southeastasia", 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_rule_collection_group/locals.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | azure_regions = [ 4 | "westeurope", 5 | "northeurope", 6 | "eastus", 7 | "eastus2", 8 | "westus3", 9 | "westus2", 10 | "southcentralus", 11 | "canadacentral", 12 | "centralus", 13 | "eastasia", 14 | "southeastasia", 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/default/variables.tf: -------------------------------------------------------------------------------- 1 | variable "enable_telemetry" { 2 | type = bool 3 | default = true 4 | description = <" 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.5" 3 | 4 | required_providers { 5 | azapi = { 6 | source = "Azure/azapi" 7 | version = "~> 2.4" 8 | } 9 | azurerm = { 10 | source = "hashicorp/azurerm" 11 | version = ">= 3.71, < 5.0.0" 12 | } 13 | modtm = { 14 | source = "Azure/modtm" 15 | version = "~> 0.3" 16 | } 17 | random = { 18 | source = "hashicorp/random" 19 | version = "~> 3.5" 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": { 3 | "terraform-mcp-eva": { 4 | "type": "stdio", 5 | "command": "docker", 6 | "args": [ 7 | "run", 8 | "-i", 9 | "--rm", 10 | "-v", 11 | "${workspaceFolder}:/workspace", 12 | "-w", 13 | "/workspace", 14 | "-e", 15 | "TRANSPORT_MODE=stdio", 16 | "-e", 17 | "GITHUB_TOKEN", 18 | "--pull=always", 19 | "ghcr.io/lonegunmanb/terraform-mcp-eva" 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | - Create a directory for each example. 4 | - Create a `_header.md` file in each directory to describe the example. 5 | - See the `default` example provided as a skeleton - this must remain, but you can add others. 6 | - Run `make fmt && make docs` from the repo root to generate the required documentation. 7 | 8 | > **Note:** Examples must be deployable and idempotent. Ensure that no input variables are required to run the example and that random values are used to ensure unique resource names. E.g. use the [naming module](https://registry.terraform.io/modules/Azure/naming/azurerm/latest) to generate a unique name for a resource. 9 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "resource" { 2 | description = <<-EOT 3 | "This is the full output for Firewall Policy resource. This is the default output for the module following AVM standards. Review the examples below for the correct output to use in your module." 4 | Examples: 5 | - module.firewall_policy.resource.id 6 | - module.firewall_policy.resource.firewalls 7 | - module.firewall_policy.resource.child_policies 8 | - module.firewall_policy.resource.rule_collection_groups 9 | EOT 10 | value = azurerm_firewall_policy.this 11 | } 12 | 13 | output "resource_id" { 14 | description = "the resource id of the firewall policy" 15 | value = azurerm_firewall_policy.this.id 16 | } 17 | -------------------------------------------------------------------------------- /modules/rule_collection_groups/_header.md: -------------------------------------------------------------------------------- 1 | # Azure Firewall Policy Rule Collection Group 2 | 3 | This is the sub-module to create Rule Collection Groups in Azure Firewall Policy 4 | 5 | ## Features 6 | 7 | This module supports: 8 | 9 | - Creates Rule Collection Groups 10 | - Creates Rule Collections 11 | - Creates Network Rules, Application Rules, and NAT Rules 12 | 13 | "Major version Zero (0.y.z) is for initial development. Anything MAY change at any time. The module SHOULD NOT be considered stable till at least it is major version one (1.0.0) or greater. Changes will always be via new versions being published and no changes will be made to existing published versions. For more details please go to " 14 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/azterraform:avm-latest", 3 | "containerEnv": { 4 | "AVM_IN_CONTAINER": "true" 5 | }, 6 | "updateRemoteUserUID": true, 7 | "runArgs": [ 8 | "--cap-add=SYS_PTRACE", 9 | "--security-opt", 10 | "seccomp=unconfined", 11 | "--init", 12 | "--network=host" 13 | ], 14 | "mounts": [ 15 | "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" 16 | ], 17 | "onCreateCommand": "terraform version", 18 | "customizations": { 19 | "vscode": { 20 | "settings": { 21 | "terraform.languageServer.terraform.path": "/home/runtimeuser/tfenv/bin/terraform" 22 | }, 23 | "extensions": [ 24 | "ms-azuretools.vscode-azureterraform", 25 | "EditorConfig.EditorConfig", 26 | "hashicorp.terraform" 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /_footer.md: -------------------------------------------------------------------------------- 1 | 2 | ## Data Collection 3 | 4 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 5 | -------------------------------------------------------------------------------- /examples/default/_footer.md: -------------------------------------------------------------------------------- 1 | 2 | ## Data Collection 3 | 4 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 5 | -------------------------------------------------------------------------------- /modules/rule_collection_groups/_footer.md: -------------------------------------------------------------------------------- 1 | 2 | ## Data Collection 3 | 4 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 5 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_for_avd/_footer.md: -------------------------------------------------------------------------------- 1 | 2 | ## Data Collection 3 | 4 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 5 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_ipgroups/_footer.md: -------------------------------------------------------------------------------- 1 | 2 | ## Data Collection 3 | 4 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 5 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_rule_collection_group/_footer.md: -------------------------------------------------------------------------------- 1 | 2 | ## Data Collection 3 | 4 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit 6 | https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need 9 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 10 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 14 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | -------------------------------------------------------------------------------- /.github/workflows/pr-check.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PR Check 3 | on: 4 | pull_request: 5 | types: ['opened', 'reopened', 'synchronize'] 6 | merge_group: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | check: 11 | permissions: {} 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: checking for fork 15 | shell: pwsh 16 | run: | 17 | $isFork = "${{ github.event.pull_request.head.repo.fork }}" 18 | if($isFork -eq "true") { 19 | echo "### WARNING: This workflow is disabled for forked repositories. Please follow the [release branch process](https://azure.github.io/Azure-Verified-Modules/contributing/terraform/terraform-contribution-flow/#5-create-a-pull-request-to-the-upstream-repository) if end to end tests are required." >> $env:GITHUB_STEP_SUMMARY 20 | } 21 | 22 | run-managed-workflow: 23 | if: github.event.pull_request.head.repo.fork == false 24 | uses: Azure/avm-terraform-governance/.github/workflows/managed-pr-check.yml@main 25 | name: run managed workflow 26 | secrets: inherit 27 | permissions: 28 | id-token: write 29 | contents: read 30 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | > ⚠️**Note:** For the full details on the support statements, SLAs, and more for the Azure Verified Modules (AVM) initiative please visit [aka.ms/AVM/Support](https://aka.ms/avm/support) ⚠️ 4 | 5 | ## How to file issues and get help 6 | 7 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. 8 | 9 | Issues can be created and searched through for existing [issues here](../../issues). 10 | 11 | Please provide as much information as possible when filing an issue. Include screenshots or correlation IDs if possible (please redact any sensitive information). 12 | 13 | For instructions on how to get deployments and correlation ID, please follow this link [here](https://learn.microsoft.com/azure/azure-resource-manager/templates/deployment-history?tabs=azure-portal#get-deployments-and-correlation-id). 14 | 15 | We may ask you to create an Azure support request once we have triaged the issue following the process documented [here](https://learn.microsoft.com/azure/azure-portal/supportability/how-to-create-azure-support-request). 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /.github/actions/linting/action.yml: -------------------------------------------------------------------------------- 1 | author: AVM 2 | name: linting 3 | description: Tests the example supplied in the input. Needs checkout and Azure login prior. 4 | inputs: 5 | github-token: 6 | description: The GitHub token 7 | required: true 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - uses: hashicorp/setup-terraform@v2 13 | with: 14 | terraform_version: ">=1.5.0" 15 | 16 | - name: terraform init 17 | run: terraform init 18 | shell: bash 19 | 20 | - name: terraform validate 21 | run: terraform validate 22 | shell: bash 23 | 24 | - uses: terraform-linters/setup-tflint@v3 25 | name: Setup TFLint 26 | with: 27 | tflint_version: v0.48.0 28 | env: 29 | GITHUB_TOKEN: ${{ inputs.github-token }} 30 | 31 | - name: get tflint config 32 | run: | 33 | curl --header "Authorization: Bearer ${{ inputs.github-token }}" https://raw.githubusercontent.com/Azure/terraform-azurerm-avm-template/main/.tflint.hcl -o .tflint.hcl 34 | shell: bash 35 | 36 | - name: tflint init 37 | run: tflint --init 38 | shell: bash 39 | 40 | - name: tflint 41 | run: tflint 42 | shell: bash 43 | -------------------------------------------------------------------------------- /.github/actions/e2e-getexamples/action.yml: -------------------------------------------------------------------------------- 1 | author: AVM 2 | name: e2e - getexamples 3 | description: Gets example directories from `examples/` and outputs them to the next step 4 | inputs: 5 | github-token: 6 | description: The GitHub token to use for the API calls 7 | required: true 8 | outputs: 9 | examples: 10 | description: The examples to test 11 | value: ${{ steps.getexamples.outputs.examples }} 12 | runs: 13 | using: composite 14 | steps: 15 | - name: get examples 16 | id: getexamples 17 | run: | 18 | # Get latest release from GitHub API and get download URL for the Linux x64 binary 19 | URL=$(curl -sL -H "Authorization: Bearer ${{ inputs.github-token }}" https://api.github.com/repos/matt-FFFFFF/jsonls/releases/latest \ 20 | | jq -r '.assets[] | select( .name | test("linux_amd64")) | .browser_download_url') 21 | 22 | # Download the binary and extract 23 | curl -sL "$URL" | tar -xvz jsonls 24 | 25 | # Ensure exec bit set 26 | sudo chmod a+x jsonls 27 | 28 | # Move binary to path 29 | sudo mv jsonls /usr/local/bin/jsonls 30 | 31 | # Get the examples 32 | echo examples="$(jsonls -d)" >> "$GITHUB_OUTPUT" 33 | working-directory: examples 34 | shell: bash 35 | -------------------------------------------------------------------------------- /examples/default/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.3.0" 3 | 4 | required_providers { 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = ">= 3.71, < 5.0.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 3.5.0, < 4.0.0" 12 | } 13 | } 14 | } 15 | 16 | provider "azurerm" { 17 | features {} 18 | } 19 | 20 | # This picks a random region from the list of regions. 21 | resource "random_integer" "region_index" { 22 | max = length(local.azure_regions) - 1 23 | min = 0 24 | } 25 | 26 | # This ensures we have unique CAF compliant names for our resources. 27 | module "naming" { 28 | source = "Azure/naming/azurerm" 29 | version = "0.3.0" 30 | } 31 | 32 | # This is required for resource modules 33 | resource "azurerm_resource_group" "this" { 34 | location = local.azure_regions[random_integer.region_index.result] 35 | name = module.naming.resource_group.name_unique 36 | } 37 | 38 | # This is the module call 39 | module "firewall_policy" { 40 | source = "../.." 41 | 42 | location = azurerm_resource_group.this.location 43 | name = module.naming.firewall_policy.name_unique 44 | resource_group_name = azurerm_resource_group.this.name 45 | # source = "Azure/avm-res-network-firewallpolicy/azurerm" 46 | enable_telemetry = var.enable_telemetry 47 | } -------------------------------------------------------------------------------- /.terraform-docs.yml: -------------------------------------------------------------------------------- 1 | ### To generate the output file to partially incorporate in the README.md, 2 | ### Execute this command in the Terraform module's code folder: 3 | # terraform-docs -c .terraform-docs.yml . 4 | 5 | formatter: "markdown document" # this is required 6 | 7 | version: "~> 0.18" 8 | 9 | header-from: "_header.md" 10 | footer-from: "_footer.md" 11 | 12 | recursive: 13 | enabled: false 14 | path: modules 15 | 16 | sections: 17 | hide: [] 18 | show: [] 19 | 20 | content: |- 21 | 22 | {{ .Header }} 23 | 24 | 25 | {{ .Requirements }} 26 | 27 | {{ .Resources }} 28 | 29 | 30 | {{ .Inputs }} 31 | 32 | {{ .Outputs }} 33 | 34 | {{ .Modules }} 35 | 36 | {{ .Footer }} 37 | 38 | output: 39 | file: README.md 40 | mode: replace 41 | template: |- 42 | 43 | {{ .Content }} 44 | 45 | output-values: 46 | enabled: false 47 | from: "" 48 | 49 | sort: 50 | enabled: true 51 | by: required 52 | 53 | settings: 54 | anchor: true 55 | color: true 56 | default: true 57 | description: false 58 | escape: true 59 | hide-empty: false 60 | html: true 61 | indent: 2 62 | lockfile: false 63 | read-comments: true 64 | required: true 65 | sensitive: true 66 | type: true 67 | -------------------------------------------------------------------------------- /modules/.terraform-docs.yml: -------------------------------------------------------------------------------- 1 | ### To generate the output file to partially incorporate in the README.md, 2 | ### Execute this command in the Terraform module's code folder: 3 | # terraform-docs -c .terraform-docs.yml . 4 | 5 | formatter: "markdown document" # this is required 6 | 7 | version: "~> 0.18" 8 | 9 | header-from: "_header.md" 10 | footer-from: "_footer.md" 11 | 12 | recursive: 13 | enabled: false 14 | path: modules 15 | 16 | sections: 17 | hide: [] 18 | show: [] 19 | 20 | content: |- 21 | 22 | {{ .Header }} 23 | 24 | 25 | {{ .Requirements }} 26 | 27 | {{ .Resources }} 28 | 29 | 30 | {{ .Inputs }} 31 | 32 | {{ .Outputs }} 33 | 34 | {{ .Modules }} 35 | 36 | {{ .Footer }} 37 | 38 | output: 39 | file: README.md 40 | mode: replace 41 | template: |- 42 | 43 | {{ .Content }} 44 | 45 | output-values: 46 | enabled: false 47 | from: "" 48 | 49 | sort: 50 | enabled: true 51 | by: required 52 | 53 | settings: 54 | anchor: true 55 | color: true 56 | default: true 57 | description: false 58 | escape: true 59 | hide-empty: false 60 | html: true 61 | indent: 2 62 | lockfile: false 63 | read-comments: true 64 | required: true 65 | sensitive: true 66 | type: true 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | crash.*.log 11 | 12 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 13 | # password, private keys, and other secrets. These should not be part of version 14 | # control as they are data points which are potentially sensitive and subject 15 | # to change depending on the environment. 16 | *.tfvars 17 | *.tfvars.json 18 | 19 | # Ignore override files as they are usually used to override resources locally and so 20 | # are not checked in 21 | override.tf 22 | override.tf.json 23 | *_override.tf 24 | *_override.tf.json 25 | 26 | # Include override files you do wish to add to version control using negated pattern 27 | # !example_override.tf 28 | 29 | # Ignore Terraform lock file 30 | .terraform.lock.hcl 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | 35 | # Ignore CLI configuration files 36 | .terraformrc 37 | terraform.rc 38 | *tfplan* 39 | .terraform.lock.hcl 40 | README-generated.md 41 | avm.tflint.hcl 42 | avm.tflint.merged.hcl 43 | avm.tflint_example.hcl 44 | avm.tflint_example.merged.hcl 45 | avmmakefile 46 | *.md.tmp 47 | .DS_Store 48 | avm.tflint_module.hcl 49 | avm.tflint_module.merged.hcl 50 | examples/*/policy 51 | *.mptfbackup 52 | .avm 53 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 12 | 13 | ## Type of Change 14 | 15 | 16 | 17 | - [ ] Non-module change (e.g. CI/CD, documentation, etc.) 18 | - [ ] Azure Verified Module updates: 19 | - [ ] Bugfix containing backwards compatible bug fixes 20 | - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. 21 | - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. 22 | - [ ] Feature update backwards compatible feature updates. 23 | - [ ] Breaking changes. 24 | - [ ] Update to documentation 25 | 26 | # Checklist 27 | 28 | - [ ] I'm sure there are no other open Pull Requests for the same update/change 29 | - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings 30 | - [ ] I did run all [pre-commit](https://azure.github.io/Azure-Verified-Modules/contributing/terraform/terraform-contribution-flow/#5-run-pre-commit-checks) checks 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/.terraform-docs.yml: -------------------------------------------------------------------------------- 1 | ### To generate the output file to partially incorporate in the README.md, 2 | ### Execute this command in the Terraform module's code folder: 3 | # terraform-docs -c .terraform-docs.yml . 4 | 5 | formatter: "markdown document" # this is required 6 | 7 | version: "~> 0.18" 8 | 9 | header-from: "_header.md" 10 | footer-from: "_footer.md" 11 | 12 | recursive: 13 | enabled: false 14 | path: modules 15 | 16 | sections: 17 | hide: [] 18 | show: [] 19 | 20 | content: |- 21 | 22 | {{ .Header }} 23 | 24 | ```hcl 25 | {{ include "main.tf" }} 26 | ``` 27 | 28 | 29 | {{ .Requirements }} 30 | 31 | {{ .Resources }} 32 | 33 | 34 | {{ .Inputs }} 35 | 36 | {{ .Outputs }} 37 | 38 | {{ .Modules }} 39 | 40 | {{ .Footer }} 41 | output: 42 | file: README.md 43 | mode: replace 44 | template: |- 45 | 46 | {{ .Content }} 47 | 48 | output-values: 49 | enabled: false 50 | from: "" 51 | 52 | sort: 53 | enabled: true 54 | by: required 55 | 56 | settings: 57 | anchor: true 58 | color: true 59 | default: true 60 | description: false 61 | escape: true 62 | hide-empty: false 63 | html: true 64 | indent: 2 65 | lockfile: false 66 | read-comments: true 67 | required: true 68 | sensitive: true 69 | type: true 70 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/avm_question_feedback.yml: -------------------------------------------------------------------------------- 1 | name: AVM - General Question/Feedback ❔ 2 | description: Just got a question or some general feedback? Let us know! 3 | title: "[AVM Question/Feedback]: " 4 | labels: 5 | [ 6 | "Language: Terraform :globe_with_meridians:", 7 | "Type: Question/Feedback :raising_hand:", 8 | "Needs: Triage :mag:", 9 | ] 10 | projects: ["Azure/538"] 11 | body: 12 | - type: markdown 13 | attributes: 14 | value: | 15 | Thank you for your question/feedback! 16 | 17 | > **NOTE**: If your question/request is related to the AVM site/documentation, please file an issue in the [AVM repo](https://github.com/Azure/Azure-Verified-Modules/issues/new?assignees=&labels=Type%3A+Question%2FFeedback+%3Araising_hand%3A%2CNeeds%3A+Triage+%3Amag%3A&projects=&template=question_feedback.yml&title=%5BQuestion%2FFeedback%5D%3A+). 18 | - type: checkboxes 19 | id: existing-checks 20 | attributes: 21 | label: Check for previous/existing GitHub issues 22 | description: By submitting this issue, you confirm that you have searched for previous/existing GitHub issues to avoid creating a duplicate. 23 | options: 24 | - label: I have checked for previous/existing GitHub issues 25 | required: true 26 | - type: textarea 27 | id: question-feedback-text 28 | attributes: 29 | label: Description 30 | description: Let us know your question or feedback here! 31 | validations: 32 | required: true -------------------------------------------------------------------------------- /.github/actions/e2e-testexamples/action.yml: -------------------------------------------------------------------------------- 1 | author: AVM 2 | name: e2e - testexamples 3 | description: Tests the example supplied in the input. Needs checkout and Azure login prior. 4 | inputs: 5 | example: 6 | description: The example directory to test 7 | required: true 8 | 9 | runs: 10 | using: composite 11 | steps: 12 | - uses: hashicorp/setup-terraform@v2 13 | with: 14 | terraform_version: ">=1.5.0" 15 | 16 | - name: terraform init 17 | run: terraform init 18 | working-directory: examples/${{ inputs.example }} 19 | shell: bash 20 | 21 | - name: terraform apply 22 | run: terraform apply -auto-approve 23 | working-directory: examples/${{ inputs.example }} 24 | shell: bash 25 | 26 | - name: terraform plan 27 | id: plan 28 | run: | 29 | terraform plan -detailed-exitcode 30 | echo PLANCODE="$?" >> "$GITHUB_OUTPUT" 31 | continue-on-error: true 32 | working-directory: examples/${{ inputs.example }} 33 | shell: bash 34 | 35 | - name: check idempotent 36 | run: | 37 | echo Error: terraform plan code is ${{ steps.plan.outputs.PLANCODE }} 38 | exit 1 39 | working-directory: examples/${{ inputs.example }} 40 | shell: bash 41 | if: steps.plan.outputs.PLANCODE != 0 42 | 43 | - name: terraform destroy 44 | run: terraform destroy -auto-approve 45 | working-directory: examples/${{ inputs.example }} 46 | shell: bash 47 | if: always() 48 | -------------------------------------------------------------------------------- /.github/actions/docs-check/action.yml: -------------------------------------------------------------------------------- 1 | author: AVM 2 | name: Docs check 3 | description: Checks that documentation has been updated on PR 4 | runs: 5 | using: composite 6 | steps: 7 | - name: setup go 8 | uses: actions/setup-go@v4 9 | with: 10 | go-version: "1.21.x" 11 | # cache-dependency-path: tests/go.sum 12 | 13 | - name: setup Terraform 14 | uses: hashicorp/setup-terraform@v2 15 | with: 16 | terraform_wrapper: false 17 | 18 | - name: install tools 19 | shell: bash 20 | run: | 21 | go install github.com/katbyte/terrafmt@latest 22 | go install github.com/terraform-docs/terraform-docs@latest 23 | 24 | - name: fmt check 25 | shell: bash 26 | run: | 27 | echo "==> Fixing Terraform code with terraform fmt..." 28 | terraform fmt -recursive 29 | echo "==> Fixing embedded Terraform with terrafmt..." 30 | find . | egrep ".md|.tf" | grep -v README.md | sort | while read f; do terrafmt fmt $$f; done 31 | 32 | - name: docs check 33 | shell: bash 34 | run: | 35 | echo "==> Generating module documentation..." 36 | terraform-docs -c .terraform-docs.yml . 37 | echo "==> Generating examples documentation..." 38 | cd examples && for d in $(ls -d */); do terraform-docs $d; done 39 | 40 | - name: check for changes 41 | shell: bash 42 | run: | 43 | echo "==> Testing for changes to tracked files" 44 | CHANGES=$(git status -suno) 45 | if [ "$CHANGES" ]; then 46 | echo "Repository formatting or documentation is not correct." 47 | echo 48 | git diff 49 | echo 50 | echo "Run 'make fmt && make docs' locally and commit the changes to fix." 51 | exit 1 52 | fi 53 | -------------------------------------------------------------------------------- /main.telemetry.tf: -------------------------------------------------------------------------------- 1 | 2 | data "modtm_module_source" "telemetry" { 3 | count = var.enable_telemetry ? 1 : 0 4 | 5 | module_path = path.module 6 | } 7 | 8 | resource "random_uuid" "telemetry" { 9 | count = var.enable_telemetry ? 1 : 0 10 | } 11 | 12 | resource "modtm_telemetry" "telemetry" { 13 | count = var.enable_telemetry ? 1 : 0 14 | 15 | tags = merge({ 16 | subscription_id = one(data.azapi_client_config.telemetry).subscription_id 17 | tenant_id = one(data.azapi_client_config.telemetry).tenant_id 18 | module_source = one(data.modtm_module_source.telemetry).module_source 19 | module_version = one(data.modtm_module_source.telemetry).module_version 20 | random_id = one(random_uuid.telemetry).result 21 | }, { location = local.main_location }) 22 | } 23 | data "azapi_client_config" "telemetry" { 24 | count = var.enable_telemetry ? 1 : 0 25 | } 26 | 27 | locals { 28 | valid_module_source_regex = [ 29 | "registry.terraform.io/[A|a]zure/.+", 30 | "registry.opentofu.io/[A|a]zure/.+", 31 | "git::https://github\\.com/[A|a]zure/.+", 32 | "git::ssh:://git@github\\.com/[A|a]zure/.+", 33 | ] 34 | } 35 | 36 | locals { 37 | avm_azapi_headers = !var.enable_telemetry ? {} : (local.fork_avm ? { 38 | fork_avm = "true" 39 | random_id = one(random_uuid.telemetry).result 40 | } : { 41 | avm = "true" 42 | random_id = one(random_uuid.telemetry).result 43 | avm_module_source = one(data.modtm_module_source.telemetry).module_source 44 | avm_module_version = one(data.modtm_module_source.telemetry).module_version 45 | }) 46 | } 47 | 48 | locals { 49 | fork_avm = !anytrue([for r in local.valid_module_source_regex : can(regex(r, one(data.modtm_module_source.telemetry).module_source))]) 50 | } 51 | 52 | locals { 53 | # tflint-ignore: terraform_unused_declarations 54 | avm_azapi_header = join(" ", [for k, v in local.avm_azapi_headers : "${k}=${v}"]) 55 | } 56 | locals { 57 | main_location = var.location 58 | } 59 | 60 | -------------------------------------------------------------------------------- /avmmakefile: -------------------------------------------------------------------------------- 1 | AVM_PORCH_BASE_URL := git::https://github.com/Azure/avm-terraform-governance//porch-configs 2 | AVM_PORCH_REF := main 3 | 4 | .PHONY: help 5 | help: 6 | @echo "please use 'make '" 7 | @echo "available targets are:" 8 | @echo " help" 9 | @echo " migrate (a no-op, this repo has already been migrated)" 10 | @echo " pre-commit (runs doc generation and repo file sync)" 11 | @echo " pr-check (checks that pre-commit has been run and runs linters)" 12 | @echo " test-examples (tests all examples - set `AVM_EXAMPLE` to the specific example name to only test one)" 13 | @echo " tf-test-unit (runs unit tests in `tests/unit`)" 14 | @echo " tf-test-integration (runs integration tests in `tests/integration`)" 15 | @echo " globalsetup (runs global setup tasks, only if you have a `examples/setup.sh`)" 16 | @echo " globalteardown (runs global teardown tasks, only if you have a `examples/teardown.sh`)" 17 | 18 | .PHONY: migrate 19 | migrate: 20 | @echo "This is a no-op. This repo has already been migrated." 21 | 22 | .PHONY: pre-commit 23 | pre-commit: 24 | @echo "Running pre-commit..." 25 | porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/pre-commit.porch.yaml?ref=$(AVM_PORCH_REF)" 26 | 27 | .PHONY: pr-check 28 | pr-check: 29 | @echo "Running PR check..." 30 | porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/pr-check.porch.yaml?ref=$(AVM_PORCH_REF)" 31 | 32 | .PHONY: test-examples 33 | test-examples: 34 | @echo "Testing examples..." 35 | porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/test-examples.porch.yaml?ref=$(AVM_PORCH_REF)" 36 | 37 | .PHONY: tf-test-unit 38 | tf-test-unit: 39 | @echo "Running terraform unit test..." 40 | AVM_TEST_TYPE="unit" porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/terraform-test.porch.yaml?ref=$(AVM_PORCH_REF)" 41 | 42 | .PHONY: tf-test-integration 43 | tf-test-integration: 44 | @echo "Running terraform integration test..." 45 | AVM_TEST_TYPE="integration" porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/terraform-test.porch.yaml?ref=$(AVM_PORCH_REF)" 46 | 47 | .PHONY: globalsetup 48 | globalsetup: 49 | @echo "Running global setup..." 50 | porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/global-setup.porch.yaml?ref=$(AVM_PORCH_REF)" 51 | 52 | .PHONY: globalteardown 53 | globalteardown: 54 | @echo "Running global teardown..." 55 | porch run ${TUI} -f "$(AVM_PORCH_BASE_URL)/global-teardown.porch.yaml?ref=$(AVM_PORCH_REF)" 56 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/avm_module_issue.yml: -------------------------------------------------------------------------------- 1 | name: AVM - Module Issue ➕🐛🔒 2 | description: Want to request a new Module feature or report a bug? Let us know! 3 | title: "[AVM Module Issue]: " 4 | labels: ["Needs: Triage :mag:", "Language: Terraform :globe_with_meridians:"] 5 | projects: ["Azure/566"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for submitting this AVM Module Issue! To help us triage your issue, please provide the below details. 11 | 12 | > **NOTE**: If you'd like to propose a new AVM module, please file an [AVM Module Proposal](https://aka.ms/AVM/ModuleProposal). 13 | - type: checkboxes 14 | id: existing-checks 15 | attributes: 16 | label: Check for previous/existing GitHub issues 17 | description: By submitting this issue, you confirm that you have searched for previous/existing GitHub issues to avoid creating a duplicate. 18 | options: 19 | - label: I have checked for previous/existing GitHub issues 20 | required: true 21 | - type: dropdown 22 | id: issue-type 23 | attributes: 24 | label: Issue Type? 25 | description: How would you best describe this issue? Is this a... 26 | options: 27 | - "" 28 | - "Feature Request" 29 | - "Bug" 30 | - "I'm not sure" 31 | validations: 32 | required: true 33 | - type: input 34 | id: module-version 35 | attributes: 36 | label: (Optional) Module Version 37 | description: Please provide which version(s) of the module does this issue apply to. 38 | validations: 39 | required: false 40 | - type: input 41 | id: correlation-id 42 | attributes: 43 | label: (Optional) Correlation Id 44 | description: Please provide a correlation id if available and appropriate. 45 | validations: 46 | required: false 47 | - type: textarea 48 | id: question-feedback-text 49 | attributes: 50 | label: Description 51 | description: | 52 | Please describe the issue! 53 | > **NOTE**: All requested features must already be supported by the provider and Preview Services ([SFR1](https://azure.github.io/Azure-Verified-Modules/specs/shared/#id-sfr1---category-composition---preview-services)) are not supported. 54 | placeholder: | 55 | 59 | validations: 60 | required: true 61 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_rule_collection_group/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.3.0" 3 | 4 | required_providers { 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = ">= 3.71, < 5.0.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 3.5.0, < 4.0.0" 12 | } 13 | } 14 | } 15 | 16 | provider "azurerm" { 17 | features {} 18 | } 19 | 20 | # This picks a random region from the list of regions. 21 | resource "random_integer" "region_index" { 22 | max = length(local.azure_regions) - 1 23 | min = 0 24 | } 25 | 26 | # This ensures we have unique CAF compliant names for our resources. 27 | module "naming" { 28 | source = "Azure/naming/azurerm" 29 | version = "0.3.0" 30 | } 31 | 32 | # This is required for resource modules 33 | resource "azurerm_resource_group" "this" { 34 | location = local.azure_regions[random_integer.region_index.result] 35 | name = module.naming.resource_group.name_unique 36 | } 37 | 38 | # This is the module call 39 | module "firewall_policy" { 40 | source = "../.." 41 | 42 | location = azurerm_resource_group.this.location 43 | name = module.naming.firewall_policy.name_unique 44 | resource_group_name = azurerm_resource_group.this.name 45 | # source = "Azure/avm-res-network-firewallpolicy/azurerm" 46 | enable_telemetry = var.enable_telemetry 47 | } 48 | 49 | module "rule_collection_group" { 50 | source = "../../modules/rule_collection_groups" 51 | 52 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 53 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 54 | firewall_policy_rule_collection_group_name = "NetworkRuleCollectionGroup" 55 | firewall_policy_rule_collection_group_priority = 400 56 | firewall_policy_rule_collection_group_application_rule_collection = [ 57 | { 58 | action = "Allow" 59 | name = "ApplicationRuleCollection" 60 | priority = 600 61 | rule = [ 62 | { 63 | name = "AllowAll" 64 | description = "Allow traffic to Microsoft.com" 65 | source_addresses = ["10.0.0.0/24"] 66 | protocols = [ 67 | { 68 | port = 443 69 | type = "Https" 70 | } 71 | ] 72 | destination_fqdns = ["microsoft.com"] 73 | } 74 | ] 75 | } 76 | ] 77 | firewall_policy_rule_collection_group_network_rule_collection = [ 78 | { 79 | action = "Allow" 80 | name = "NetworkRuleCollection" 81 | priority = 400 82 | rule = [ 83 | { 84 | name = "OutboundToInternet" 85 | description = "Allow traffic outbound to the Internet" 86 | destination_addresses = ["0.0.0.0/0"] 87 | destination_ports = ["443"] 88 | source_addresses = ["10.0.0.0/24"] 89 | protocols = ["TCP"] 90 | } 91 | ] 92 | } 93 | ] 94 | } -------------------------------------------------------------------------------- /examples/default/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Default example 4 | 5 | This deploys the module in its simplest form. 6 | 7 | ```hcl 8 | terraform { 9 | required_version = ">= 1.3.0" 10 | 11 | required_providers { 12 | azurerm = { 13 | source = "hashicorp/azurerm" 14 | version = ">= 3.71, < 5.0.0" 15 | } 16 | random = { 17 | source = "hashicorp/random" 18 | version = ">= 3.5.0, < 4.0.0" 19 | } 20 | } 21 | } 22 | 23 | provider "azurerm" { 24 | features {} 25 | } 26 | 27 | # This picks a random region from the list of regions. 28 | resource "random_integer" "region_index" { 29 | max = length(local.azure_regions) - 1 30 | min = 0 31 | } 32 | 33 | # This ensures we have unique CAF compliant names for our resources. 34 | module "naming" { 35 | source = "Azure/naming/azurerm" 36 | version = "0.3.0" 37 | } 38 | 39 | # This is required for resource modules 40 | resource "azurerm_resource_group" "this" { 41 | location = local.azure_regions[random_integer.region_index.result] 42 | name = module.naming.resource_group.name_unique 43 | } 44 | 45 | # This is the module call 46 | module "firewall_policy" { 47 | source = "../.." 48 | 49 | location = azurerm_resource_group.this.location 50 | name = module.naming.firewall_policy.name_unique 51 | resource_group_name = azurerm_resource_group.this.name 52 | # source = "Azure/avm-res-network-firewallpolicy/azurerm" 53 | enable_telemetry = var.enable_telemetry 54 | } 55 | ``` 56 | 57 | 58 | ## Requirements 59 | 60 | The following requirements are needed by this module: 61 | 62 | - [terraform](#requirement\_terraform) (>= 1.3.0) 63 | 64 | - [azurerm](#requirement\_azurerm) (>= 3.71, < 5.0.0) 65 | 66 | - [random](#requirement\_random) (>= 3.5.0, < 4.0.0) 67 | 68 | ## Resources 69 | 70 | The following resources are used by this module: 71 | 72 | - [azurerm_resource_group.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) (resource) 73 | - [random_integer.region_index](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) (resource) 74 | 75 | 76 | ## Required Inputs 77 | 78 | No required inputs. 79 | 80 | ## Optional Inputs 81 | 82 | The following input variables are optional (have default values): 83 | 84 | ### [enable\_telemetry](#input\_enable\_telemetry) 85 | 86 | Description: This variable controls whether or not telemetry is enabled for the module. 87 | For more information see https://aka.ms/avm/telemetryinfo. 88 | If it is set to false, then no telemetry will be collected. 89 | 90 | Type: `bool` 91 | 92 | Default: `true` 93 | 94 | ## Outputs 95 | 96 | No outputs. 97 | 98 | ## Modules 99 | 100 | The following Modules are called: 101 | 102 | ### [firewall\_policy](#module\_firewall\_policy) 103 | 104 | Source: ../.. 105 | 106 | Version: 107 | 108 | ### [naming](#module\_naming) 109 | 110 | Source: Azure/naming/azurerm 111 | 112 | Version: 0.3.0 113 | 114 | 115 | ## Data Collection 116 | 117 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 118 | -------------------------------------------------------------------------------- /modules/rule_collection_groups/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_firewall_policy_rule_collection_group" "this" { 2 | firewall_policy_id = var.firewall_policy_rule_collection_group_firewall_policy_id 3 | name = var.firewall_policy_rule_collection_group_name 4 | priority = var.firewall_policy_rule_collection_group_priority 5 | 6 | dynamic "application_rule_collection" { 7 | for_each = var.firewall_policy_rule_collection_group_application_rule_collection == null ? [] : var.firewall_policy_rule_collection_group_application_rule_collection 8 | 9 | content { 10 | action = application_rule_collection.value.action 11 | name = application_rule_collection.value.name 12 | priority = application_rule_collection.value.priority 13 | 14 | dynamic "rule" { 15 | for_each = application_rule_collection.value.rule 16 | 17 | content { 18 | name = rule.value.name 19 | description = rule.value.description 20 | destination_addresses = rule.value.destination_addresses 21 | destination_fqdn_tags = rule.value.destination_fqdn_tags 22 | destination_fqdns = rule.value.destination_fqdns 23 | destination_urls = rule.value.destination_urls 24 | source_addresses = rule.value.source_addresses 25 | source_ip_groups = rule.value.source_ip_groups 26 | terminate_tls = rule.value.terminate_tls 27 | web_categories = rule.value.web_categories 28 | 29 | dynamic "http_headers" { 30 | for_each = rule.value.http_headers == null ? [] : rule.value.http_headers 31 | 32 | content { 33 | name = http_headers.value.name 34 | value = http_headers.value.value 35 | } 36 | } 37 | dynamic "protocols" { 38 | for_each = rule.value.protocols == null ? [] : rule.value.protocols 39 | 40 | content { 41 | port = protocols.value.port 42 | type = protocols.value.type 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | dynamic "nat_rule_collection" { 50 | for_each = var.firewall_policy_rule_collection_group_nat_rule_collection == null ? [] : var.firewall_policy_rule_collection_group_nat_rule_collection 51 | 52 | content { 53 | action = nat_rule_collection.value.action 54 | name = nat_rule_collection.value.name 55 | priority = nat_rule_collection.value.priority 56 | 57 | dynamic "rule" { 58 | for_each = nat_rule_collection.value.rule 59 | 60 | content { 61 | name = rule.value.name 62 | protocols = rule.value.protocols 63 | translated_port = rule.value.translated_port 64 | destination_address = rule.value.destination_address 65 | destination_ports = rule.value.destination_ports 66 | source_addresses = rule.value.source_addresses 67 | source_ip_groups = rule.value.source_ip_groups 68 | translated_address = rule.value.translated_address 69 | translated_fqdn = rule.value.translated_fqdn 70 | } 71 | } 72 | } 73 | } 74 | dynamic "network_rule_collection" { 75 | for_each = var.firewall_policy_rule_collection_group_network_rule_collection == null ? [] : var.firewall_policy_rule_collection_group_network_rule_collection 76 | 77 | content { 78 | action = network_rule_collection.value.action 79 | name = network_rule_collection.value.name 80 | priority = network_rule_collection.value.priority 81 | 82 | dynamic "rule" { 83 | for_each = network_rule_collection.value.rule 84 | 85 | content { 86 | destination_ports = rule.value.destination_ports 87 | name = rule.value.name 88 | protocols = rule.value.protocols 89 | destination_addresses = rule.value.destination_addresses 90 | destination_fqdns = rule.value.destination_fqdns 91 | destination_ip_groups = rule.value.destination_ip_groups 92 | source_addresses = rule.value.source_addresses 93 | source_ip_groups = rule.value.source_ip_groups 94 | } 95 | } 96 | } 97 | } 98 | dynamic "timeouts" { 99 | for_each = var.firewall_policy_rule_collection_group_timeouts == null ? [] : [var.firewall_policy_rule_collection_group_timeouts] 100 | 101 | content { 102 | create = timeouts.value.create 103 | delete = timeouts.value.delete 104 | read = timeouts.value.read 105 | update = timeouts.value.update 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /avm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | usage () { 6 | echo "Usage: avm " 7 | } 8 | 9 | # We need to do this because bash doesn't like it when a script is updated in place. 10 | if [ -z ${AVM_SCRIPT_FORKED} ]; then 11 | # If AVM_SCRIPT_FORKED is not set, we are running the script from the original repository 12 | # Set AVM_SCRIPT_FORKED to true to avoid running this block again 13 | export AVM_SCRIPT_FORKED=true 14 | 15 | # Make a copy of this script in the current directory 16 | # and run that copy. 17 | cp "$0" .avm 18 | chmod +x .avm 19 | exec ./.avm "$@" 20 | fi 21 | 22 | # Default values for environment variables 23 | CONTAINER_RUNTIME=${CONTAINER_RUNTIME:-"docker"} 24 | CONTAINER_IMAGE=${CONTAINER_IMAGE:-"mcr.microsoft.com/azterraform:avm-latest"} 25 | CONTAINER_PULL_POLICY=${CONTAINER_PULL_POLICY:-"always"} 26 | AVM_MAKEFILE_REF=${AVM_MAKEFILE_REF:-"main"} 27 | AVM_PORCH_REF=${AVM_PORCH_REF:-"main"} 28 | 29 | if [ ! "$(command -v "${CONTAINER_RUNTIME}")" ] && [ -z "${AVM_IN_CONTAINER}" ]; then 30 | echo "Error: ${CONTAINER_RUNTIME} is not installed. Please install ${CONTAINER_RUNTIME} first." 31 | exit 1 32 | fi 33 | 34 | if [ -z "$1" ]; then 35 | echo "Error: Please provide a make target. See https://github.com/Azure/avm-terraform-governance/blob/main/Makefile for available targets." 36 | echo 37 | usage 38 | exit 1 39 | fi 40 | 41 | # Check if AZURE_CONFIG_DIR is set, if not, set it to ~/.azure 42 | if [ -z "${AZURE_CONFIG_DIR}" ]; then 43 | AZURE_CONFIG_DIR="${HOME}/.azure" 44 | fi 45 | 46 | # Check if AZURE_CONFIG_DIR exists, if it does, mount it to the container 47 | if [ -d "${AZURE_CONFIG_DIR}" ]; then 48 | AZURE_CONFIG_MOUNT="-v ${AZURE_CONFIG_DIR}:/home/runtimeuser/.azure" 49 | fi 50 | 51 | # If the host Docker socket exists, mount it into the container so the container can talk to the host docker daemon 52 | if [ -S /var/run/docker.sock ]; then 53 | DOCKER_SOCK_MOUNT="-v /var/run/docker.sock:/var/run/docker.sock" 54 | fi 55 | 56 | # If we are in GitHub Copilot Coding Agent, we need to mount the SSL certificates from the host 57 | SSL_CERT_MOUNTS="" 58 | if [ -n "${COPILOT_AGENT_ACTION}" ]; then 59 | # Mount host's CA bundle to container's expected paths 60 | SSL_CERT_MOUNTS="${SSL_CERT_MOUNTS} -v /etc/ssl/certs/ca-certificates.crt:/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:ro" 61 | SSL_CERT_MOUNTS="${SSL_CERT_MOUNTS} -v /etc/ssl/certs/ca-certificates.crt:/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt:ro" 62 | fi 63 | 64 | # New: allow overriding TUI behavior with PORCH_FORCE_TUI and PORCH_NO_TUI environment variables. 65 | # - If PORCH_FORCE_TUI is set, force TUI and interactive mode (even in GH Actions). 66 | # - If PORCH_NO_TUI is set, explicitly disable TUI. 67 | # - Otherwise, fallback to previous behavior: enable TUI only when not in GitHub Actions and NO_COLOR is not set. 68 | if [ -n "${PORCH_FORCE_TUI}" ]; then 69 | TUI="--tui" 70 | DOCKER_INTERACTIVE="-it" 71 | export FORCE_COLOR=1 72 | elif [ -n "${PORCH_NO_TUI}" ]; then 73 | # Explicitly disable TUI and interactive flags 74 | TUI="" 75 | DOCKER_INTERACTIVE="" 76 | else 77 | # If we are not in GitHub Actions and NO_COLOR is not set, we want to use TUI and interactive mode 78 | if [ -z "${GITHUB_RUN_ID}" ] && [ -z "${NO_COLOR}" ]; then 79 | TUI="--tui" 80 | DOCKER_INTERACTIVE="-it" 81 | export FORCE_COLOR=1 82 | fi 83 | fi 84 | 85 | # if AVM_PORCH_BASE_URL is set, we want to add it to the make command 86 | if [ -n "${AVM_PORCH_BASE_URL}" ]; then 87 | PORCH_BASE_URL_MAKE_ADD="PORCH_BASE_URL=${AVM_PORCH_BASE_URL}" 88 | fi 89 | 90 | # Check if we are running in a container 91 | # If we are then just run make directly 92 | if [ -z "${AVM_IN_CONTAINER}" ]; then 93 | ${CONTAINER_RUNTIME} run \ 94 | --pull "${CONTAINER_PULL_POLICY}" \ 95 | --user "$(id -u):$(id -g)" \ 96 | --rm \ 97 | ${DOCKER_INTERACTIVE} \ 98 | -v "$(pwd)":/src \ 99 | ${AZURE_CONFIG_MOUNT:-} \ 100 | ${DOCKER_SOCK_MOUNT:-} \ 101 | ${SSL_CERT_MOUNTS:-} \ 102 | -e ARM_CLIENT_ID \ 103 | -e ARM_OIDC_REQUEST_TOKEN \ 104 | -e ARM_OIDC_REQUEST_URL \ 105 | -e ARM_SUBSCRIPTION_ID \ 106 | -e ARM_TENANT_ID \ 107 | -e ARM_USE_OIDC \ 108 | -e FORCE_COLOR \ 109 | -e GITHUB_TOKEN \ 110 | -e NO_COLOR \ 111 | -e PORCH_LOG_LEVEL \ 112 | -e TF_IN_AUTOMATION=1 \ 113 | --env-file <(env | grep '^TF_VAR_') \ 114 | --env-file <(env | grep '^AVM_') \ 115 | "${CONTAINER_IMAGE}" \ 116 | make \ 117 | TUI="${TUI}" \ 118 | AVM_MAKEFILE_REF="${AVM_MAKEFILE_REF}" \ 119 | "${PORCH_BASE_URL_MAKE_ADD}" \ 120 | AVM_PORCH_REF="${AVM_PORCH_REF}" \ 121 | "$1" 122 | else 123 | make TUI="${TUI}" AVM_MAKEFILE_REF="${AVM_MAKEFILE_REF}" ${PORCH_BASE_URL_MAKE_ADD} AVM_PORCH_REF="${AVM_PORCH_REF}" "$1" 124 | fi 125 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_ipgroups/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.3.0" 3 | 4 | required_providers { 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = ">= 3.71, < 5.0.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 3.5.0, < 4.0.0" 12 | } 13 | } 14 | } 15 | 16 | provider "azurerm" { 17 | features {} 18 | } 19 | 20 | # This picks a random region from the list of regions. 21 | resource "random_integer" "region_index" { 22 | max = length(local.azure_regions) - 1 23 | min = 0 24 | } 25 | 26 | # This ensures we have unique CAF compliant names for our resources. 27 | module "naming" { 28 | source = "Azure/naming/azurerm" 29 | version = "0.3.0" 30 | } 31 | 32 | # This is required for resource modules 33 | resource "azurerm_resource_group" "rg" { 34 | location = local.azure_regions[random_integer.region_index.result] 35 | name = module.naming.resource_group.name_unique 36 | } 37 | 38 | module "vnet" { 39 | source = "Azure/avm-res-network-virtualnetwork/azurerm" 40 | version = "0.16.0" 41 | 42 | location = azurerm_resource_group.rg.location 43 | parent_id = azurerm_resource_group.rg.id 44 | address_space = ["10.1.0.0/16"] 45 | enable_telemetry = var.enable_telemetry 46 | name = module.naming.virtual_network.name_unique 47 | } 48 | 49 | resource "azurerm_subnet" "subnet" { 50 | address_prefixes = ["10.1.0.0/26"] 51 | name = "AzureFirewallSubnet" 52 | resource_group_name = azurerm_resource_group.rg.name 53 | virtual_network_name = module.vnet.resource.name 54 | } 55 | 56 | resource "azurerm_public_ip" "pip" { 57 | allocation_method = "Static" 58 | location = azurerm_resource_group.rg.location 59 | name = "pip" 60 | resource_group_name = azurerm_resource_group.rg.name 61 | sku = "Standard" 62 | zones = ["1", "2", "3"] 63 | } 64 | 65 | resource "azurerm_ip_group" "ipgroup_1" { 66 | location = azurerm_resource_group.rg.location 67 | name = "ipgroup1" 68 | resource_group_name = azurerm_resource_group.rg.name 69 | cidrs = ["192.168.0.1", "172.16.240.0/20", "10.48.0.0/12"] 70 | } 71 | 72 | resource "azurerm_ip_group" "ipgroup_2" { 73 | location = azurerm_resource_group.rg.location 74 | name = "ipgroup2" 75 | resource_group_name = azurerm_resource_group.rg.name 76 | cidrs = ["10.100.10.0/24", "192.100.10.4", "10.150.20.20"] 77 | } 78 | 79 | # This is the module call 80 | module "firewall" { 81 | source = "Azure/avm-res-network-azurefirewall/azurerm" 82 | version = "0.4.0" 83 | 84 | firewall_sku_name = "AZFW_VNet" 85 | firewall_sku_tier = "Standard" 86 | location = azurerm_resource_group.rg.location 87 | name = module.naming.firewall.name 88 | resource_group_name = azurerm_resource_group.rg.name 89 | enable_telemetry = var.enable_telemetry 90 | firewall_ip_configuration = [ 91 | { 92 | name = "ipconfig1" 93 | subnet_id = azurerm_subnet.subnet.id 94 | public_ip_address_id = azurerm_public_ip.pip.id 95 | } 96 | ] 97 | firewall_policy_id = module.firewall_policy.resource.id 98 | firewall_zones = ["1", "2", "3"] 99 | } 100 | 101 | module "firewall_policy" { 102 | source = "../.." 103 | 104 | location = azurerm_resource_group.rg.location 105 | name = module.naming.firewall_policy.name 106 | resource_group_name = azurerm_resource_group.rg.name 107 | enable_telemetry = var.enable_telemetry 108 | } 109 | 110 | module "rule_collection_group" { 111 | source = "../../modules/rule_collection_groups" 112 | 113 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 114 | firewall_policy_rule_collection_group_name = "IPGroupRCG" 115 | firewall_policy_rule_collection_group_priority = 400 116 | firewall_policy_rule_collection_group_application_rule_collection = [ 117 | { 118 | action = "Allow" 119 | name = "ApplicationRuleCollection" 120 | priority = 201 121 | rule = [ 122 | { 123 | name = "AllowMicrosoft" 124 | destination_fqdns = ["*.microsoft.com"] 125 | source_ip_groups = [azurerm_ip_group.ipgroup_2.id] 126 | protocols = [ 127 | { 128 | port = 443 129 | type = "Https" 130 | } 131 | ] 132 | } 133 | ] 134 | } 135 | ] 136 | firewall_policy_rule_collection_group_network_rule_collection = [ 137 | { 138 | action = "Allow" 139 | name = "NetworkRuleCollection" 140 | priority = 101 141 | rule = [ 142 | { 143 | name = "OutboundToIPGroups" 144 | destination_ports = ["443"] 145 | destination_ip_groups = [azurerm_ip_group.ipgroup_1.id] 146 | source_ip_groups = [azurerm_ip_group.ipgroup_2.id] 147 | protocols = ["TCP"] 148 | } 149 | ] 150 | } 151 | ] 152 | } 153 | -------------------------------------------------------------------------------- /avm.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | [CmdletBinding()] 4 | param( 5 | [Parameter(Position = 0, Mandatory = $true)] 6 | [string]$Target 7 | ) 8 | 9 | Set-StrictMode -Version 3.0 10 | $ErrorActionPreference = "Stop" 11 | 12 | function Show-Usage { 13 | Write-Host "Usage: avm " 14 | } 15 | 16 | # Default values for environment variables 17 | $CONTAINER_RUNTIME = if ($env:CONTAINER_RUNTIME) { $env:CONTAINER_RUNTIME } else { "docker" } 18 | $CONTAINER_IMAGE = if ($env:CONTAINER_IMAGE) { $env:CONTAINER_IMAGE } else { "mcr.microsoft.com/azterraform:avm-latest" } 19 | $CONTAINER_PULL_POLICY = if ($env:CONTAINER_PULL_POLICY) { $env:CONTAINER_PULL_POLICY } else { "always" } 20 | $MAKEFILE_REF = if ($env:MAKEFILE_REF) { $env:MAKEFILE_REF } else { "main" } 21 | $PORCH_REF = if ($env:PORCH_REF) { $env:PORCH_REF } else { "main" } 22 | 23 | # Check if container runtime is available 24 | if (-not (Get-Command $CONTAINER_RUNTIME -ErrorAction SilentlyContinue) -and -not $env:AVM_IN_CONTAINER) { 25 | Write-Error "Error: $CONTAINER_RUNTIME is not installed. Please install $CONTAINER_RUNTIME first." 26 | exit 1 27 | } 28 | 29 | # Check if AZURE_CONFIG_DIR is set, if not, set it to ~/.azure 30 | $AZURE_CONFIG_DIR = if ($env:AZURE_CONFIG_DIR) { 31 | $env:AZURE_CONFIG_DIR 32 | } 33 | else { 34 | if ($IsWindows) { 35 | Join-Path $env:USERPROFILE ".azure" 36 | } 37 | else { 38 | Join-Path $env:HOME ".azure" 39 | } 40 | } 41 | 42 | # Check if AZURE_CONFIG_DIR exists, if it does, mount it to the container 43 | $AZURE_CONFIG_MOUNT = $null 44 | $AZURE_CONFIG_MOUNT_PATH = $null 45 | if (Test-Path $AZURE_CONFIG_DIR) { 46 | $AZURE_CONFIG_MOUNT = "-v" 47 | $AZURE_CONFIG_MOUNT_PATH = "${AZURE_CONFIG_DIR}:/home/runtimeuser/.azure" 48 | } 49 | 50 | # New: allow overriding TUI behavior with PORCH_FORCE_TUI and PORCH_NO_TUI environment variables. 51 | # - If PORCH_FORCE_TUI is set, force TUI and interactive mode (even in GH Actions). 52 | # - If PORCH_NO_TUI is set, explicitly disable TUI. 53 | # - Otherwise, fallback to previous behavior: enable TUI only when not in GitHub Actions and NO_COLOR is not set. 54 | $TUI = $null 55 | $DOCKER_INTERACTIVE = $null 56 | if ($env:PORCH_FORCE_TUI -and $env:PORCH_FORCE_TUI -ne "") { 57 | $TUI = "--tui" 58 | $DOCKER_INTERACTIVE = "-it" 59 | $env:FORCE_COLOR = "1" 60 | } 61 | elseif ($env:PORCH_NO_TUI -and $env:PORCH_NO_TUI -ne "") { 62 | # Explicitly disable TUI and interactive flags 63 | $TUI = $null 64 | $DOCKER_INTERACTIVE = $null 65 | } 66 | else { 67 | # If we are not in GitHub Actions and NO_COLOR is not set, we want to use TUI and interactive mode 68 | if (-not $env:GITHUB_RUN_ID -and -not $env:NO_COLOR) { 69 | $TUI = "--tui" 70 | $DOCKER_INTERACTIVE = "-it" 71 | $env:FORCE_COLOR = "1" 72 | } 73 | } 74 | 75 | # if PORCH_BASE_URL is set, we want to add it to the make command 76 | $PORCH_BASE_URL_MAKE_ADD = $null 77 | if ($env:PORCH_BASE_URL) { 78 | $PORCH_BASE_URL_MAKE_ADD = "PORCH_BASE_URL=$($env:PORCH_BASE_URL)" 79 | } 80 | 81 | # Check if we are running in a container 82 | # If we are then just run make directly 83 | if (-not $env:AVM_IN_CONTAINER) { 84 | # Build the docker command arguments 85 | $dockerArgs = @( 86 | "run" 87 | "--pull", $CONTAINER_PULL_POLICY 88 | "--rm" 89 | ) 90 | 91 | # Add user parameter only on Unix-like systems 92 | if (-not $IsWindows) { 93 | try { 94 | $userId = & id -u 95 | $groupId = & id -g 96 | $dockerArgs += @("--user", "${userId}:${groupId}") 97 | } 98 | catch { 99 | Write-Warning "Could not determine user/group ID, running without --user parameter" 100 | } 101 | } 102 | 103 | if ($DOCKER_INTERACTIVE) { 104 | $dockerArgs += $DOCKER_INTERACTIVE 105 | } 106 | 107 | $dockerArgs += @( 108 | "-v", "$(Get-Location):/src" 109 | ) 110 | 111 | if ($AZURE_CONFIG_MOUNT -and $AZURE_CONFIG_MOUNT_PATH) { 112 | $dockerArgs += @($AZURE_CONFIG_MOUNT, $AZURE_CONFIG_MOUNT_PATH) 113 | } 114 | 115 | # Add environment variables 116 | $envVars = @( 117 | "ARM_CLIENT_ID", 118 | "ARM_OIDC_REQUEST_TOKEN", 119 | "ARM_OIDC_REQUEST_URL", 120 | "ARM_SUBSCRIPTION_ID", 121 | "ARM_TENANT_ID", 122 | "ARM_USE_OIDC", 123 | "AVM_EXAMPLE", 124 | "CONFTEST_APRL_URL", 125 | "CONFTEST_AVMSEC_URL", 126 | "CONFTEST_EXCEPTIONS_URL", 127 | "FORCE_COLOR", 128 | "GITHUB_TOKEN", 129 | "GREPT_URL", 130 | "MPTF_URL", 131 | "NO_COLOR", 132 | "PORCH_LOG_LEVEL", 133 | "TEST_TYPE", 134 | "TFLINT_CONFIG_URL" 135 | ) 136 | 137 | foreach ($envVar in $envVars) { 138 | $envValue = [System.Environment]::GetEnvironmentVariable($envVar) 139 | if ($null -ne $envValue -and $envValue -ne "") { 140 | $dockerArgs += @("-e", $envVar) 141 | } 142 | } 143 | 144 | # Add TF_IN_AUTOMATION 145 | $dockerArgs += @("-e", "TF_IN_AUTOMATION=1") 146 | 147 | # Add TF_VAR_ environment variables 148 | Get-ChildItem env: | Where-Object { $_.Name -like "TF_VAR_*" } | ForEach-Object { 149 | $dockerArgs += @("-e", "$($_.Name)=$($_.Value)") 150 | } 151 | 152 | # Add AVM_ environment variables 153 | Get-ChildItem env: | Where-Object { $_.Name -like "AVM_*" } | ForEach-Object { 154 | $dockerArgs += @("-e", "$($_.Name)=$($_.Value)") 155 | } 156 | 157 | $dockerArgs += $CONTAINER_IMAGE 158 | $dockerArgs += "make" 159 | 160 | if ($TUI) { 161 | $dockerArgs += "TUI=$TUI" 162 | } 163 | 164 | $dockerArgs += "MAKEFILE_REF=$MAKEFILE_REF" 165 | 166 | if ($PORCH_BASE_URL_MAKE_ADD) { 167 | $dockerArgs += $PORCH_BASE_URL_MAKE_ADD 168 | } 169 | 170 | $dockerArgs += "PORCH_REF=$PORCH_REF" 171 | $dockerArgs += $Target 172 | 173 | & $CONTAINER_RUNTIME @dockerArgs 174 | } 175 | else { 176 | # Build make command arguments 177 | $makeArgs = @() 178 | 179 | if ($TUI) { 180 | $makeArgs += "TUI=$TUI" 181 | } 182 | 183 | $makeArgs += "MAKEFILE_REF=$MAKEFILE_REF" 184 | 185 | if ($PORCH_BASE_URL_MAKE_ADD) { 186 | $makeArgs += $PORCH_BASE_URL_MAKE_ADD 187 | } 188 | 189 | $makeArgs += "PORCH_REF=$PORCH_REF" 190 | $makeArgs += $Target 191 | 192 | & make @makeArgs 193 | } 194 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_rule_collection_group/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Deploy Azure Firewall Policy with Rule Collection Groups 4 | 5 | This example deploys Azure Firewall Policy with Rules Collection Groups 6 | 7 | - Firewall Policy 8 | - Rule Collection Groups 9 | - Rule Collections 10 | - Network and Application Rules 11 | 12 | ```hcl 13 | terraform { 14 | required_version = ">= 1.3.0" 15 | 16 | required_providers { 17 | azurerm = { 18 | source = "hashicorp/azurerm" 19 | version = ">= 3.71, < 5.0.0" 20 | } 21 | random = { 22 | source = "hashicorp/random" 23 | version = ">= 3.5.0, < 4.0.0" 24 | } 25 | } 26 | } 27 | 28 | provider "azurerm" { 29 | features {} 30 | } 31 | 32 | # This picks a random region from the list of regions. 33 | resource "random_integer" "region_index" { 34 | max = length(local.azure_regions) - 1 35 | min = 0 36 | } 37 | 38 | # This ensures we have unique CAF compliant names for our resources. 39 | module "naming" { 40 | source = "Azure/naming/azurerm" 41 | version = "0.3.0" 42 | } 43 | 44 | # This is required for resource modules 45 | resource "azurerm_resource_group" "this" { 46 | location = local.azure_regions[random_integer.region_index.result] 47 | name = module.naming.resource_group.name_unique 48 | } 49 | 50 | # This is the module call 51 | module "firewall_policy" { 52 | source = "../.." 53 | 54 | location = azurerm_resource_group.this.location 55 | name = module.naming.firewall_policy.name_unique 56 | resource_group_name = azurerm_resource_group.this.name 57 | # source = "Azure/avm-res-network-firewallpolicy/azurerm" 58 | enable_telemetry = var.enable_telemetry 59 | } 60 | 61 | module "rule_collection_group" { 62 | source = "../../modules/rule_collection_groups" 63 | 64 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 65 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 66 | firewall_policy_rule_collection_group_name = "NetworkRuleCollectionGroup" 67 | firewall_policy_rule_collection_group_priority = 400 68 | firewall_policy_rule_collection_group_application_rule_collection = [ 69 | { 70 | action = "Allow" 71 | name = "ApplicationRuleCollection" 72 | priority = 600 73 | rule = [ 74 | { 75 | name = "AllowAll" 76 | description = "Allow traffic to Microsoft.com" 77 | source_addresses = ["10.0.0.0/24"] 78 | protocols = [ 79 | { 80 | port = 443 81 | type = "Https" 82 | } 83 | ] 84 | destination_fqdns = ["microsoft.com"] 85 | } 86 | ] 87 | } 88 | ] 89 | firewall_policy_rule_collection_group_network_rule_collection = [ 90 | { 91 | action = "Allow" 92 | name = "NetworkRuleCollection" 93 | priority = 400 94 | rule = [ 95 | { 96 | name = "OutboundToInternet" 97 | description = "Allow traffic outbound to the Internet" 98 | destination_addresses = ["0.0.0.0/0"] 99 | destination_ports = ["443"] 100 | source_addresses = ["10.0.0.0/24"] 101 | protocols = ["TCP"] 102 | } 103 | ] 104 | } 105 | ] 106 | } 107 | ``` 108 | 109 | 110 | ## Requirements 111 | 112 | The following requirements are needed by this module: 113 | 114 | - [terraform](#requirement\_terraform) (>= 1.3.0) 115 | 116 | - [azurerm](#requirement\_azurerm) (>= 3.71, < 5.0.0) 117 | 118 | - [random](#requirement\_random) (>= 3.5.0, < 4.0.0) 119 | 120 | ## Resources 121 | 122 | The following resources are used by this module: 123 | 124 | - [azurerm_resource_group.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) (resource) 125 | - [random_integer.region_index](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) (resource) 126 | 127 | 128 | ## Required Inputs 129 | 130 | No required inputs. 131 | 132 | ## Optional Inputs 133 | 134 | The following input variables are optional (have default values): 135 | 136 | ### [enable\_telemetry](#input\_enable\_telemetry) 137 | 138 | Description: This variable controls whether or not telemetry is enabled for the module. 139 | For more information see https://aka.ms/avm/telemetryinfo. 140 | If it is set to false, then no telemetry will be collected. 141 | 142 | Type: `bool` 143 | 144 | Default: `true` 145 | 146 | ## Outputs 147 | 148 | No outputs. 149 | 150 | ## Modules 151 | 152 | The following Modules are called: 153 | 154 | ### [firewall\_policy](#module\_firewall\_policy) 155 | 156 | Source: ../.. 157 | 158 | Version: 159 | 160 | ### [naming](#module\_naming) 161 | 162 | Source: Azure/naming/azurerm 163 | 164 | Version: 0.3.0 165 | 166 | ### [rule\_collection\_group](#module\_rule\_collection\_group) 167 | 168 | Source: ../../modules/rule_collection_groups 169 | 170 | Version: 171 | 172 | 173 | ## Data Collection 174 | 175 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 176 | -------------------------------------------------------------------------------- /.github/policies/eventResponder.yml: -------------------------------------------------------------------------------- 1 | id: avmEventResponder 2 | name: AVM Event Responder 3 | description: AVM Event Responder 4 | resource: repository 5 | disabled: false 6 | 7 | configuration: 8 | resourceManagementConfiguration: 9 | eventResponderTasks: 10 | - description: 'ITA06 - If a new issue or PR is opened add the "Needs: Triage :mag:" label' 11 | if: 12 | - or: 13 | - payloadType: Issues 14 | - payloadType: Pull_Request 15 | - isAction: 16 | action: Opened 17 | then: 18 | - addLabel: 19 | label: "Needs: Triage :mag:" 20 | - addReply: 21 | reply: | 22 | > [!IMPORTANT] 23 | > **The "Needs: Triage :mag:" label must be removed once the triage process is complete!** 24 | 25 | > [!TIP] 26 | > For additional guidance on how to triage this issue/PR, see the [Terraform Issue Triage](https://azure.github.io/Azure-Verified-Modules/help-support/issue-triage/terraform-issue-triage) documentation. 27 | 28 | - description: 'ITA09 - When #RR is used in an issue, add the "Needs: Author Feedback :ear:" label' 29 | if: 30 | - or: 31 | - payloadType: Pull_Request_Review_Comment 32 | - payloadType: Issue_Comment 33 | - commentContains: 34 | pattern: "#RR" 35 | - not: 36 | hasLabel: 37 | label: "Needs: Author Feedback :ear:" 38 | then: 39 | - addLabel: 40 | label: "Needs: Author Feedback :ear:" 41 | 42 | - description: 'ITA10 - When #wontfix is used in an issue, mark it by using the label of "Status: Won''t Fix :broken_heart:"' 43 | if: 44 | - or: 45 | - payloadType: Pull_Request_Review_Comment 46 | - payloadType: Issue_Comment 47 | - commentContains: 48 | pattern: "#wontfix" 49 | - not: 50 | hasLabel: 51 | label: "Status: Won't Fix :broken_heart:" 52 | then: 53 | - addLabel: 54 | label: "Status: Won't Fix :broken_heart:" 55 | - closeIssue 56 | 57 | - description: 'ITA11 - When the author replies, remove the "Needs: Author Feedback :ear:" label and label with "Needs: Attention :wave:"' 58 | if: 59 | - or: 60 | - payloadType: Pull_Request_Review_Comment 61 | - payloadType: Issue_Comment 62 | - not: 63 | isAction: 64 | action: Closed 65 | - hasLabel: 66 | label: "Needs: Author Feedback :ear:" 67 | - isActivitySender: 68 | issueAuthor: true 69 | then: 70 | - removeLabel: 71 | label: "Needs: Author Feedback :ear:" 72 | - removeLabel: 73 | label: "Status: No Recent Activity :zzz:" 74 | - addLabel: 75 | label: "Needs: Attention :wave:" 76 | 77 | - description: "ITA12 - Clean email replies on every comment" 78 | if: 79 | - payloadType: Issue_Comment 80 | then: 81 | - cleanEmailReply 82 | 83 | - description: 'ITA15 - remove the "Needs: Triage" label from a PR, if it already has a "Type: XYZ" label added at the time of creating it.' 84 | if: 85 | - payloadType: Pull_Request 86 | - isAction: 87 | action: Opened 88 | - or: 89 | - hasLabel: 90 | label: "Type: Bug :bug:" 91 | - hasLabel: 92 | label: "Type: Documentation :page_facing_up:" 93 | - hasLabel: 94 | label: "Type: Duplicate :palms_up_together:" 95 | - hasLabel: 96 | label: "Type: Feature Request :heavy_plus_sign:" 97 | - hasLabel: 98 | label: "Type: Hygiene :broom:" 99 | - hasLabel: 100 | label: "Type: New Module Proposal :bulb:" 101 | - hasLabel: 102 | label: "Type: Question/Feedback :raising_hand:" 103 | - hasLabel: 104 | label: "Type: Security Bug :lock:" 105 | - isAssignedToSomeone 106 | then: 107 | - removeLabel: 108 | label: "Needs: Triage :mag:" 109 | 110 | - description: 'ITA20 - If the type is feature request, assign the "Type: Feature Request :heavy_plus_sign:" label on the issue' 111 | if: 112 | - payloadType: Issues 113 | - isAction: 114 | action: Opened 115 | - bodyContains: 116 | pattern: | 117 | ### Issue Type? 118 | 119 | Feature Request 120 | - not: 121 | hasLabel: 122 | label: "Type: Feature Request :heavy_plus_sign:" 123 | then: 124 | - addLabel: 125 | label: "Type: Feature Request :heavy_plus_sign:" 126 | 127 | - description: 'ITA21 - If the type is bug, assign the "Type: Bug :bug:" label on the issue' 128 | if: 129 | - payloadType: Issues 130 | - isAction: 131 | action: Opened 132 | - bodyContains: 133 | pattern: | 134 | ### Issue Type? 135 | 136 | Bug 137 | - not: 138 | hasLabel: 139 | label: "Type: Bug :bug:" 140 | then: 141 | - addLabel: 142 | label: "Type: Bug :bug:" 143 | 144 | - description: 'ITA22 - If the type is security bug, assign the "Type: Security Bug :lock:" label on the issue' 145 | if: 146 | - payloadType: Issues 147 | - isAction: 148 | action: Opened 149 | - bodyContains: 150 | pattern: | 151 | ### Issue Type? 152 | 153 | Security Bug 154 | - not: 155 | hasLabel: 156 | label: "Type: Security Bug :lock:" 157 | then: 158 | - addLabel: 159 | label: "Type: Security Bug :lock:" 160 | 161 | - description: 'ITA23 - Remove the "Status: In PR" label from an issue when it''s closed.' 162 | if: 163 | - payloadType: Issues 164 | - isAction: 165 | action: Closed 166 | - hasLabel: 167 | label: "Status: In PR :point_right:" 168 | then: 169 | - removeLabel: 170 | label: "Status: In PR :point_right:" 171 | -------------------------------------------------------------------------------- /modules/rule_collection_groups/variables.tf: -------------------------------------------------------------------------------- 1 | variable "firewall_policy_rule_collection_group_firewall_policy_id" { 2 | type = string 3 | description = "(Required) The ID of the Firewall Policy where the Firewall Policy Rule Collection Group should exist. Changing this forces a new Firewall Policy Rule Collection Group to be created." 4 | nullable = false 5 | } 6 | 7 | variable "firewall_policy_rule_collection_group_name" { 8 | type = string 9 | description = "(Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created." 10 | nullable = false 11 | } 12 | 13 | variable "firewall_policy_rule_collection_group_priority" { 14 | type = number 15 | description = "(Required) The priority of the Firewall Policy Rule Collection Group. The range is 100-65000." 16 | nullable = false 17 | } 18 | 19 | variable "firewall_policy_rule_collection_group_application_rule_collection" { 20 | type = list(object({ 21 | action = string 22 | name = string 23 | priority = number 24 | rule = list(object({ 25 | description = optional(string) 26 | destination_addresses = optional(list(string), []) 27 | destination_fqdn_tags = optional(list(string), []) 28 | destination_fqdns = optional(list(string), []) 29 | destination_urls = optional(list(string), []) 30 | name = string 31 | source_addresses = optional(list(string), []) 32 | source_ip_groups = optional(list(string), []) 33 | terminate_tls = optional(bool) 34 | web_categories = optional(list(string), []) 35 | http_headers = optional(list(object({ 36 | name = string 37 | value = string 38 | }))) 39 | protocols = optional(list(object({ 40 | port = number 41 | type = string 42 | }))) 43 | })) 44 | })) 45 | default = null 46 | description = <<-EOT 47 | - `action` - (Required) The action to take for the application rules in this collection. Possible values are `Allow` and `Deny`. 48 | - `name` - (Required) The name which should be used for this application rule collection. 49 | - `priority` - (Required) The priority of the application rule collection. The range is `100` 50 | 51 | --- 52 | `rule` block supports the following: 53 | - `description` - 54 | - `destination_addresses` - 55 | - `destination_fqdn_tags` - 56 | - `destination_fqdns` - 57 | - `destination_urls` - 58 | - `name` - (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 59 | - `source_addresses` - 60 | - `source_ip_groups` - 61 | - `terminate_tls` - 62 | - `web_categories` - 63 | 64 | --- 65 | `http_headers` block supports the following: 66 | - `name` - (Required) Specifies the name of the header. 67 | - `value` - (Required) Specifies the value of the value. 68 | 69 | --- 70 | `protocols` block supports the following: 71 | - `port` - (Required) Port number of the protocol. Range is 0-64000. 72 | - `type` - (Required) Protocol type. Possible values are `Http` and `Https`. 73 | EOT 74 | } 75 | 76 | variable "firewall_policy_rule_collection_group_nat_rule_collection" { 77 | type = list(object({ 78 | action = string 79 | name = string 80 | priority = number 81 | rule = list(object({ 82 | description = optional(string) 83 | destination_address = optional(string) 84 | destination_ports = optional(list(string), []) 85 | name = string 86 | protocols = list(string) 87 | source_addresses = optional(list(string), []) 88 | source_ip_groups = optional(list(string), []) 89 | translated_address = optional(string) 90 | translated_fqdn = optional(string) 91 | translated_port = number 92 | })) 93 | })) 94 | default = null 95 | description = <<-EOT 96 | - `action` - (Required) The action to take for the NAT rules in this collection. Currently, the only possible value is `Dnat`. 97 | - `name` - (Required) The name which should be used for this NAT rule collection. 98 | - `priority` - (Required) The priority of the NAT rule collection. The range is `100` 99 | 100 | --- 101 | `rule` block supports the following: 102 | - `description` - 103 | - `destination_address` - 104 | - `destination_ports` - 105 | - `name` - (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 106 | - `protocols` - 107 | - `source_addresses` - 108 | - `source_ip_groups` - 109 | - `translated_address` - 110 | - `translated_fqdn` - 111 | - `translated_port` - 112 | EOT 113 | } 114 | 115 | variable "firewall_policy_rule_collection_group_network_rule_collection" { 116 | type = list(object({ 117 | action = string 118 | name = string 119 | priority = number 120 | rule = list(object({ 121 | description = optional(string) 122 | destination_addresses = optional(list(string), []) 123 | destination_fqdns = optional(list(string), []) 124 | destination_ip_groups = optional(list(string), []) 125 | destination_ports = list(string) 126 | name = string 127 | protocols = list(string) 128 | source_addresses = optional(list(string), []) 129 | source_ip_groups = optional(list(string), []) 130 | })) 131 | })) 132 | default = null 133 | description = <<-EOT 134 | - `action` - (Required) The action to take for the network rules in this collection. Possible values are `Allow` and `Deny`. 135 | - `name` - (Required) The name which should be used for this network rule collection. 136 | - `priority` - (Required) The priority of the network rule collection. The range is `100` 137 | 138 | --- 139 | `rule` block supports the following: 140 | - `description` - 141 | - `destination_addresses` - 142 | - `destination_fqdns` - 143 | - `destination_ip_groups` - 144 | - `destination_ports` - 145 | - `name` - (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 146 | - `protocols` - 147 | - `source_addresses` - 148 | - `source_ip_groups` - 149 | EOT 150 | } 151 | 152 | variable "firewall_policy_rule_collection_group_timeouts" { 153 | type = object({ 154 | create = optional(string) 155 | delete = optional(string) 156 | read = optional(string) 157 | update = optional(string) 158 | }) 159 | default = null 160 | description = <<-EOT 161 | - `create` - (Defaults to 30 minutes) Used when creating the Firewall Policy Rule Collection Group. 162 | - `delete` - (Defaults to 30 minutes) Used when deleting the Firewall Policy Rule Collection Group. 163 | - `read` - (Defaults to 5 minutes) Used when retrieving the Firewall Policy Rule Collection Group. 164 | - `update` - (Defaults to 30 minutes) Used when updating the Firewall Policy Rule Collection Group. 165 | EOT 166 | } 167 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_firewall_policy" "this" { 2 | location = var.location 3 | name = var.name 4 | resource_group_name = var.resource_group_name 5 | auto_learn_private_ranges_enabled = var.firewall_policy_auto_learn_private_ranges_enabled 6 | base_policy_id = var.firewall_policy_base_policy_id 7 | private_ip_ranges = var.firewall_policy_private_ip_ranges 8 | sku = var.firewall_policy_sku 9 | sql_redirect_allowed = var.firewall_policy_sql_redirect_allowed 10 | tags = var.tags 11 | threat_intelligence_mode = var.firewall_policy_threat_intelligence_mode 12 | 13 | dynamic "dns" { 14 | for_each = var.firewall_policy_dns == null ? [] : [var.firewall_policy_dns] 15 | 16 | content { 17 | proxy_enabled = dns.value.proxy_enabled 18 | servers = dns.value.servers 19 | } 20 | } 21 | dynamic "explicit_proxy" { 22 | for_each = var.firewall_policy_explicit_proxy == null ? [] : [var.firewall_policy_explicit_proxy] 23 | 24 | content { 25 | enable_pac_file = explicit_proxy.value.enable_pac_file 26 | enabled = explicit_proxy.value.enabled 27 | http_port = explicit_proxy.value.http_port 28 | https_port = explicit_proxy.value.https_port 29 | pac_file = explicit_proxy.value.pac_file 30 | pac_file_port = explicit_proxy.value.pac_file_port 31 | } 32 | } 33 | dynamic "identity" { 34 | for_each = var.firewall_policy_identity == null ? [] : [var.firewall_policy_identity] 35 | 36 | content { 37 | type = identity.value.type 38 | identity_ids = identity.value.identity_ids 39 | } 40 | } 41 | dynamic "insights" { 42 | for_each = var.firewall_policy_insights == null ? [] : [var.firewall_policy_insights] 43 | 44 | content { 45 | default_log_analytics_workspace_id = insights.value.default_log_analytics_workspace_id 46 | enabled = insights.value.enabled 47 | retention_in_days = insights.value.retention_in_days 48 | 49 | dynamic "log_analytics_workspace" { 50 | for_each = insights.value.log_analytics_workspace == null ? [] : insights.value.log_analytics_workspace 51 | 52 | content { 53 | firewall_location = log_analytics_workspace.value.firewall_location 54 | id = log_analytics_workspace.value.id 55 | } 56 | } 57 | } 58 | } 59 | dynamic "intrusion_detection" { 60 | for_each = var.firewall_policy_intrusion_detection == null ? [] : [var.firewall_policy_intrusion_detection] 61 | 62 | content { 63 | mode = intrusion_detection.value.mode 64 | private_ranges = intrusion_detection.value.private_ranges 65 | 66 | dynamic "signature_overrides" { 67 | for_each = intrusion_detection.value.signature_overrides == null ? [] : intrusion_detection.value.signature_overrides 68 | 69 | content { 70 | id = signature_overrides.value.id 71 | state = signature_overrides.value.state 72 | } 73 | } 74 | dynamic "traffic_bypass" { 75 | for_each = intrusion_detection.value.traffic_bypass == null ? [] : intrusion_detection.value.traffic_bypass 76 | 77 | content { 78 | name = traffic_bypass.value.name 79 | protocol = traffic_bypass.value.protocol 80 | description = traffic_bypass.value.description 81 | destination_addresses = traffic_bypass.value.destination_addresses 82 | destination_ip_groups = traffic_bypass.value.destination_ip_groups 83 | destination_ports = traffic_bypass.value.destination_ports 84 | source_addresses = traffic_bypass.value.source_addresses 85 | source_ip_groups = traffic_bypass.value.source_ip_groups 86 | } 87 | } 88 | } 89 | } 90 | dynamic "threat_intelligence_allowlist" { 91 | for_each = var.firewall_policy_threat_intelligence_allowlist == null ? [] : [var.firewall_policy_threat_intelligence_allowlist] 92 | 93 | content { 94 | fqdns = threat_intelligence_allowlist.value.fqdns 95 | ip_addresses = threat_intelligence_allowlist.value.ip_addresses 96 | } 97 | } 98 | dynamic "timeouts" { 99 | for_each = var.firewall_policy_timeouts == null ? [] : [var.firewall_policy_timeouts] 100 | 101 | content { 102 | create = timeouts.value.create 103 | delete = timeouts.value.delete 104 | read = timeouts.value.read 105 | update = timeouts.value.update 106 | } 107 | } 108 | dynamic "tls_certificate" { 109 | for_each = var.firewall_policy_tls_certificate == null ? [] : [var.firewall_policy_tls_certificate] 110 | 111 | content { 112 | key_vault_secret_id = tls_certificate.value.key_vault_secret_id 113 | name = tls_certificate.value.name 114 | } 115 | } 116 | } 117 | 118 | # Assigning Roles to the Virtual Network based on the provided configurations. 119 | resource "azurerm_role_assignment" "this" { 120 | for_each = var.role_assignments 121 | 122 | principal_id = each.value.principal_id 123 | scope = azurerm_firewall_policy.this.id 124 | condition = each.value.condition 125 | condition_version = each.value.condition_version 126 | delegated_managed_identity_resource_id = each.value.delegated_managed_identity_resource_id 127 | role_definition_id = strcontains(lower(each.value.role_definition_id_or_name), lower(local.role_definition_resource_substring)) ? each.value.role_definition_id_or_name : null 128 | role_definition_name = strcontains(lower(each.value.role_definition_id_or_name), lower(local.role_definition_resource_substring)) ? null : each.value.role_definition_id_or_name 129 | skip_service_principal_aad_check = each.value.skip_service_principal_aad_check 130 | } 131 | 132 | resource "azurerm_monitor_diagnostic_setting" "this" { 133 | for_each = var.diagnostic_settings 134 | 135 | name = each.value.name != null ? each.value.name : "diag-${var.name}" 136 | target_resource_id = azurerm_firewall_policy.this.id 137 | eventhub_authorization_rule_id = each.value.event_hub_authorization_rule_resource_id 138 | eventhub_name = each.value.event_hub_name 139 | log_analytics_destination_type = each.value.log_analytics_destination_type 140 | log_analytics_workspace_id = each.value.workspace_resource_id 141 | partner_solution_id = each.value.marketplace_partner_resource_id 142 | storage_account_id = each.value.storage_account_resource_id 143 | 144 | dynamic "enabled_log" { 145 | for_each = each.value.log_categories 146 | 147 | content { 148 | category = enabled_log.value 149 | } 150 | } 151 | dynamic "enabled_log" { 152 | for_each = each.value.log_groups 153 | 154 | content { 155 | category_group = enabled_log.value 156 | } 157 | } 158 | dynamic "metric" { 159 | for_each = each.value.metric_categories 160 | 161 | content { 162 | category = metric.value 163 | } 164 | } 165 | } 166 | 167 | # required AVM resources interfaces 168 | resource "azurerm_management_lock" "this" { 169 | count = var.lock != null ? 1 : 0 170 | 171 | lock_level = var.lock.kind 172 | name = coalesce(var.lock.name, "lock-${var.lock.kind}") 173 | scope = azurerm_firewall_policy.this.id # TODO: Replace with your azurerm resource name 174 | notes = var.lock.kind == "CanNotDelete" ? "Cannot delete the resource or its child resources." : "Cannot delete or modify the resource or its child resources." 175 | } -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: ' Azure Verified Modules (AVM) and Terraform' 3 | applyTo: '**/*.terraform, **/*.tf, **/*.tfvars, **/*.tfstate, **/*.tflint.hcl, **/*.tf.json, **/*.tfvars.json' 4 | --- 5 | 6 | # Azure Verified Modules (AVM) Terraform 7 | 8 | ## Overview 9 | 10 | Azure Verified Modules (AVM) are pre-built, tested, and validated Terraform and Bicep modules that follow Azure best practices. Use these modules to create, update, or review Azure Infrastructure as Code (IaC) with confidence. 11 | 12 | ## Custom Instructions for GitHub Copilot Agents 13 | 14 | **IMPORTANT**: When GitHub Copilot Agent or GitHub Copilot Coding Agent is working on this repository, the following local unit tests MUST be executed to comply with PR checks. Failure to run these tests will cause PR validation failures: 15 | 16 | ```bash 17 | PORCH_NO_TUI=1 ./avm pre-commit 18 | git add . && git commit -m "chore: avm pre-commit" 19 | PORCH_NO_TUI=1 ./avm pr-check 20 | ``` 21 | 22 | These commands must be run before any pull request is created or updated to ensure compliance with the Azure Verified Modules standards and prevent CI/CD pipeline failures. 23 | More details on the AVM process can be found in the [Azure Verified Modules Contribution documentation](https://azure.github.io/Azure-Verified-Modules/contributing/terraform/testing/). 24 | 25 | **Failure to run these tests will cause PR validation failures and prevent successful merges.** 26 | 27 | ## Module Discovery 28 | 29 | ### Terraform Registry 30 | 31 | - Search for "avm" + resource name 32 | - Filter by "Partner" tag to find official AVM modules 33 | - Example: Search "avm storage account" → filter by Partner 34 | 35 | ### Official AVM Index 36 | 37 | - **Terraform Resources**: `https://azure.github.io/Azure-Verified-Modules/indexes/terraform/tf-resource-modules/` 38 | - **Terraform Patterns**: `https://azure.github.io/Azure-Verified-Modules/indexes/terraform/tf-pattern-modules/` 39 | - **Bicep Resources**: `https://azure.github.io/Azure-Verified-Modules/indexes/bicep/bicep-resource-modules/` 40 | - **Bicep Patterns**: `https://azure.github.io/Azure-Verified-Modules/indexes/bicep/bicep-pattern-modules/` 41 | 42 | ## Terraform Module Usage 43 | 44 | ### From Examples 45 | 46 | 1. Copy the example code from the module documentation 47 | 2. Replace `source = "../../"` with `source = "Azure/avm-res-{service}-{resource}/azurerm"` 48 | 3. Add `version = "1.0.0"` (use latest available) 49 | 4. Set `enable_telemetry = true` 50 | 51 | ### From Scratch 52 | 53 | 1. Copy the Provision Instructions from module documentation 54 | 2. Configure required and optional inputs 55 | 3. Pin the module version 56 | 4. Enable telemetry 57 | 58 | ### Example Usage 59 | 60 | ```hcl 61 | module "storage_account" { 62 | source = "Azure/avm-res-storage-storageaccount/azurerm" 63 | version = "0.1.0" 64 | 65 | enable_telemetry = true 66 | location = "East US" 67 | name = "mystorageaccount" 68 | resource_group_name = "my-rg" 69 | 70 | # Additional configuration... 71 | } 72 | ``` 73 | 74 | ## Naming Conventions 75 | 76 | ### Module Types 77 | 78 | - **Resource Modules**: `Azure/avm-res-{service}-{resource}/azurerm` 79 | - Example: `Azure/avm-res-storage-storageaccount/azurerm` 80 | - **Pattern Modules**: `Azure/avm-ptn-{pattern}/azurerm` 81 | - Example: `Azure/avm-ptn-aks-enterprise/azurerm` 82 | - **Utility Modules**: `Azure/avm-utl-{utility}/azurerm` 83 | - Example: `Azure/avm-utl-regions/azurerm` 84 | 85 | ### Service Naming 86 | 87 | - Use kebab-case for services and resources 88 | - Follow Azure service names (e.g., `storage-storageaccount`, `network-virtualnetwork`) 89 | 90 | ## Version Management 91 | 92 | ### Check Available Versions 93 | 94 | - Endpoint: `https://registry.terraform.io/v1/modules/Azure/{module}/azurerm/versions` 95 | - Example: `https://registry.terraform.io/v1/modules/Azure/avm-res-storage-storageaccount/azurerm/versions` 96 | 97 | ### Version Pinning Best Practices 98 | 99 | - For providers: use pessimistic version constraints for minor version: `version = "~> 1.0"` 100 | - For modules: Pin to specific versions: `version = "1.2.3"` 101 | 102 | ## Module Sources 103 | 104 | ### Terraform Registry 105 | 106 | - **URL Pattern**: `https://registry.terraform.io/modules/Azure/{module}/azurerm/latest` 107 | - **Example**: `https://registry.terraform.io/modules/Azure/avm-res-storage-storageaccount/azurerm/latest` 108 | 109 | ### GitHub Repository 110 | 111 | - **URL Pattern**: `https://github.com/Azure/terraform-azurerm-avm-{type}-{service}-{resource}` 112 | - **Examples**: 113 | - Resource: `https://github.com/Azure/terraform-azurerm-avm-res-storage-storageaccount` 114 | - Pattern: `https://github.com/Azure/terraform-azurerm-avm-ptn-aks-enterprise` 115 | 116 | ## Development Best Practices 117 | 118 | ### Module Usage 119 | 120 | - ✅ **Always** pin module versions 121 | - ✅ **Start** with official examples from module documentation 122 | - ✅ **Review** all inputs and outputs before implementation 123 | - ✅ **Enable** telemetry: `enable_telemetry = true` 124 | - ✅ **Use** AVM utility modules for common patterns 125 | 126 | ### Code Quality 127 | 128 | - ✅ **Always** run `terraform fmt` after making changes 129 | - ✅ **Always** run `terraform validate` after making changes 130 | - ✅ **Use** meaningful variable names and descriptions 131 | - ✅ **Use** snake_case 132 | - ✅ **Add** proper tags and metadata 133 | - ✅ **Document** complex configurations 134 | 135 | ### Validation Requirements 136 | 137 | Before creating or updating any pull request: 138 | 139 | ```bash 140 | # Format code 141 | terraform fmt -recursive 142 | 143 | # Validate syntax 144 | terraform validate 145 | 146 | # AVM-specific validation (MANDATORY) 147 | export PORCH_NO_TUI=1 148 | ./avm pre-commit 149 | 150 | ./avm pr-check 151 | ``` 152 | 153 | ## Tool Integration 154 | 155 | ### Use Available Tools 156 | 157 | - **Deployment Guidance**: Use `azure_get_deployment_best_practices` tool 158 | - **Service Documentation**: Use `microsoft.docs.mcp` tool for Azure service-specific guidance 159 | - **Schema Information**: Use `query_azapi_resource_schema` & `query_azapi_resource_document` to query AzAPI resources and schemas. 160 | - **Provider resources and resource schemas**: Use `list_terraform_provider_items` & `query_terraform_schema` to query azurerm resource schema. 161 | 162 | ### GitHub Copilot Integration 163 | 164 | When working with AVM repositories: 165 | 166 | 1. Always check for existing modules before creating new resources 167 | 2. Use the official examples as starting points 168 | 3. Run all validation tests before committing 169 | 4. Document any customizations or deviations from examples 170 | 171 | ## Common Patterns 172 | 173 | ### Resource Group Module 174 | 175 | ```hcl 176 | module "resource_group" { 177 | source = "Azure/avm-res-resources-resourcegroup/azurerm" 178 | version = "0.1.0" # use latest 179 | 180 | enable_telemetry = true 181 | location = var.location 182 | name = var.resource_group_name 183 | } 184 | ``` 185 | 186 | ### Virtual Network Module 187 | 188 | ```hcl 189 | module "virtual_network" { 190 | source = "Azure/avm-res-network-virtualnetwork/azurerm" 191 | version = "0.1.0" # use latest 192 | 193 | enable_telemetry = true 194 | location = module.resource_group.location 195 | name = var.vnet_name 196 | resource_group_name = module.resource_group.name 197 | address_space = ["10.0.0.0/16"] 198 | } 199 | ``` 200 | 201 | ## Troubleshooting 202 | 203 | ### Common Issues 204 | 205 | 1. **Version Conflicts**: Always check compatibility between module and provider versions 206 | 2. **Missing Dependencies**: Ensure all required resources are created first 207 | 3. **Validation Failures**: Run AVM validation tools before committing 208 | 4. **Documentation**: Always refer to the latest module documentation 209 | 210 | ### Support Resources 211 | 212 | - **AVM Documentation**: `https://azure.github.io/Azure-Verified-Modules/` 213 | - **GitHub Issues**: Report issues in the specific module's GitHub repository 214 | - **Community**: Azure Terraform Provider GitHub discussions 215 | 216 | ## Compliance Checklist 217 | 218 | Before submitting any AVM-related code: 219 | 220 | - [ ] Module version is pinned 221 | - [ ] Telemetry is enabled 222 | - [ ] Code is formatted (`terraform fmt`) 223 | - [ ] Code is validated (`terraform validate`) 224 | - [ ] AVM pre-commit checks pass (`./avm pre-commit`) 225 | - [ ] AVM PR checks pass (`./avm pr-check`) 226 | - [ ] Documentation is updated 227 | - [ ] Examples are tested and working 228 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: ' Azure Verified Modules (AVM) and Terraform' 3 | applyTo: '**/*.terraform, **/*.tf, **/*.tfvars, **/*.tfstate, **/*.tflint.hcl, **/*.tf.json, **/*.tfvars.json' 4 | --- 5 | 6 | # Azure Verified Modules (AVM) Terraform 7 | 8 | ## Overview 9 | 10 | Azure Verified Modules (AVM) are pre-built, tested, and validated Terraform and Bicep modules that follow Azure best practices. Use these modules to create, update, or review Azure Infrastructure as Code (IaC) with confidence. 11 | 12 | ## Custom Instructions for GitHub Copilot Agents 13 | 14 | **IMPORTANT**: When GitHub Copilot Agent or GitHub Copilot Coding Agent is working on this repository, the following local unit tests MUST be executed to comply with PR checks. Failure to run these tests will cause PR validation failures: 15 | 16 | ```bash 17 | PORCH_NO_TUI=1 ./avm pre-commit 18 | git add . && git commit -m "chore: avm pre-commit" 19 | PORCH_NO_TUI=1 ./avm pr-check 20 | ``` 21 | 22 | These commands must be run before any pull request is created or updated to ensure compliance with the Azure Verified Modules standards and prevent CI/CD pipeline failures. 23 | More details on the AVM process can be found in the [Azure Verified Modules Contribution documentation](https://azure.github.io/Azure-Verified-Modules/contributing/terraform/testing/). 24 | 25 | **Failure to run these tests will cause PR validation failures and prevent successful merges.** 26 | 27 | ## Module Discovery 28 | 29 | ### Terraform Registry 30 | 31 | - Search for "avm" + resource name 32 | - Filter by "Partner" tag to find official AVM modules 33 | - Example: Search "avm storage account" → filter by Partner 34 | 35 | ### Official AVM Index 36 | 37 | - **Terraform Resources**: `https://azure.github.io/Azure-Verified-Modules/indexes/terraform/tf-resource-modules/` 38 | - **Terraform Patterns**: `https://azure.github.io/Azure-Verified-Modules/indexes/terraform/tf-pattern-modules/` 39 | - **Bicep Resources**: `https://azure.github.io/Azure-Verified-Modules/indexes/bicep/bicep-resource-modules/` 40 | - **Bicep Patterns**: `https://azure.github.io/Azure-Verified-Modules/indexes/bicep/bicep-pattern-modules/` 41 | 42 | ## Terraform Module Usage 43 | 44 | ### From Examples 45 | 46 | 1. Copy the example code from the module documentation 47 | 2. Replace `source = "../../"` with `source = "Azure/avm-res-{service}-{resource}/azurerm"` 48 | 3. Add `version = "1.0.0"` (use latest available) 49 | 4. Set `enable_telemetry = true` 50 | 51 | ### From Scratch 52 | 53 | 1. Copy the Provision Instructions from module documentation 54 | 2. Configure required and optional inputs 55 | 3. Pin the module version 56 | 4. Enable telemetry 57 | 58 | ### Example Usage 59 | 60 | ```hcl 61 | module "storage_account" { 62 | source = "Azure/avm-res-storage-storageaccount/azurerm" 63 | version = "0.1.0" 64 | 65 | enable_telemetry = true 66 | location = "East US" 67 | name = "mystorageaccount" 68 | resource_group_name = "my-rg" 69 | 70 | # Additional configuration... 71 | } 72 | ``` 73 | 74 | ## Naming Conventions 75 | 76 | ### Module Types 77 | 78 | - **Resource Modules**: `Azure/avm-res-{service}-{resource}/azurerm` 79 | - Example: `Azure/avm-res-storage-storageaccount/azurerm` 80 | - **Pattern Modules**: `Azure/avm-ptn-{pattern}/azurerm` 81 | - Example: `Azure/avm-ptn-aks-enterprise/azurerm` 82 | - **Utility Modules**: `Azure/avm-utl-{utility}/azurerm` 83 | - Example: `Azure/avm-utl-regions/azurerm` 84 | 85 | ### Service Naming 86 | 87 | - Use kebab-case for services and resources 88 | - Follow Azure service names (e.g., `storage-storageaccount`, `network-virtualnetwork`) 89 | 90 | ## Version Management 91 | 92 | ### Check Available Versions 93 | 94 | - Endpoint: `https://registry.terraform.io/v1/modules/Azure/{module}/azurerm/versions` 95 | - Example: `https://registry.terraform.io/v1/modules/Azure/avm-res-storage-storageaccount/azurerm/versions` 96 | 97 | ### Version Pinning Best Practices 98 | 99 | - For providers: use pessimistic version constraints for minor version: `version = "~> 1.0"` 100 | - For modules: Pin to specific versions: `version = "1.2.3"` 101 | 102 | ## Module Sources 103 | 104 | ### Terraform Registry 105 | 106 | - **URL Pattern**: `https://registry.terraform.io/modules/Azure/{module}/azurerm/latest` 107 | - **Example**: `https://registry.terraform.io/modules/Azure/avm-res-storage-storageaccount/azurerm/latest` 108 | 109 | ### GitHub Repository 110 | 111 | - **URL Pattern**: `https://github.com/Azure/terraform-azurerm-avm-{type}-{service}-{resource}` 112 | - **Examples**: 113 | - Resource: `https://github.com/Azure/terraform-azurerm-avm-res-storage-storageaccount` 114 | - Pattern: `https://github.com/Azure/terraform-azurerm-avm-ptn-aks-enterprise` 115 | 116 | ## Development Best Practices 117 | 118 | ### Module Usage 119 | 120 | - ✅ **Always** pin module versions 121 | - ✅ **Start** with official examples from module documentation 122 | - ✅ **Review** all inputs and outputs before implementation 123 | - ✅ **Enable** telemetry: `enable_telemetry = true` 124 | - ✅ **Use** AVM utility modules for common patterns 125 | 126 | ### Code Quality 127 | 128 | - ✅ **Always** run `terraform fmt` after making changes 129 | - ✅ **Always** run `terraform validate` after making changes 130 | - ✅ **Use** meaningful variable names and descriptions 131 | - ✅ **Use** snake_case 132 | - ✅ **Add** proper tags and metadata 133 | - ✅ **Document** complex configurations 134 | 135 | ### Validation Requirements 136 | 137 | Before creating or updating any pull request: 138 | 139 | ```bash 140 | # Format code 141 | terraform fmt -recursive 142 | 143 | # Validate syntax 144 | terraform validate 145 | 146 | # AVM-specific validation (MANDATORY) 147 | export PORCH_NO_TUI=1 148 | ./avm pre-commit 149 | 150 | ./avm pr-check 151 | ``` 152 | 153 | ## Tool Integration 154 | 155 | ### Use Available Tools 156 | 157 | - **Deployment Guidance**: Use `azure_get_deployment_best_practices` tool 158 | - **Service Documentation**: Use `microsoft.docs.mcp` tool for Azure service-specific guidance 159 | - **Schema Information**: Use `query_azapi_resource_schema` & `query_azapi_resource_document` to query AzAPI resources and schemas. 160 | - **Provider resources and resource schemas**: Use `list_terraform_provider_items` & `query_terraform_schema` to query azurerm resource schema. 161 | 162 | ### GitHub Copilot Integration 163 | 164 | When working with AVM repositories: 165 | 166 | 1. Always check for existing modules before creating new resources 167 | 2. Use the official examples as starting points 168 | 3. Run all validation tests before committing 169 | 4. Document any customizations or deviations from examples 170 | 171 | ## Common Patterns 172 | 173 | ### Resource Group Module 174 | 175 | ```hcl 176 | module "resource_group" { 177 | source = "Azure/avm-res-resources-resourcegroup/azurerm" 178 | version = "0.1.0" # use latest 179 | 180 | enable_telemetry = true 181 | location = var.location 182 | name = var.resource_group_name 183 | } 184 | ``` 185 | 186 | ### Virtual Network Module 187 | 188 | ```hcl 189 | module "virtual_network" { 190 | source = "Azure/avm-res-network-virtualnetwork/azurerm" 191 | version = "0.1.0" # use latest 192 | 193 | enable_telemetry = true 194 | location = module.resource_group.location 195 | name = var.vnet_name 196 | resource_group_name = module.resource_group.name 197 | address_space = ["10.0.0.0/16"] 198 | } 199 | ``` 200 | 201 | ## Troubleshooting 202 | 203 | ### Common Issues 204 | 205 | 1. **Version Conflicts**: Always check compatibility between module and provider versions 206 | 2. **Missing Dependencies**: Ensure all required resources are created first 207 | 3. **Validation Failures**: Run AVM validation tools before committing 208 | 4. **Documentation**: Always refer to the latest module documentation 209 | 210 | ### Support Resources 211 | 212 | - **AVM Documentation**: `https://azure.github.io/Azure-Verified-Modules/` 213 | - **GitHub Issues**: Report issues in the specific module's GitHub repository 214 | - **Community**: Azure Terraform Provider GitHub discussions 215 | 216 | ## Compliance Checklist 217 | 218 | Before submitting any AVM-related code: 219 | 220 | - [ ] Module version is pinned 221 | - [ ] Telemetry is enabled 222 | - [ ] Code is formatted (`terraform fmt`) 223 | - [ ] Code is validated (`terraform validate`) 224 | - [ ] AVM pre-commit checks pass (`./avm pre-commit`) 225 | - [ ] AVM PR checks pass (`./avm pr-check`) 226 | - [ ] Documentation is updated 227 | - [ ] Examples are tested and working 228 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_with_ipgroups/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Deploy Firewall Policy with IP Groups 4 | 5 | This deploys an Azure Firewall Policy with IP Groups in the rules 6 | 7 | - Firewall 8 | - Firewall Policy 9 | - Rule Collection Groups 10 | - Rule Collections 11 | - Network and Application Rules 12 | - IP Groups 13 | 14 | ```hcl 15 | terraform { 16 | required_version = ">= 1.3.0" 17 | 18 | required_providers { 19 | azurerm = { 20 | source = "hashicorp/azurerm" 21 | version = ">= 3.71, < 5.0.0" 22 | } 23 | random = { 24 | source = "hashicorp/random" 25 | version = ">= 3.5.0, < 4.0.0" 26 | } 27 | } 28 | } 29 | 30 | provider "azurerm" { 31 | features {} 32 | } 33 | 34 | # This picks a random region from the list of regions. 35 | resource "random_integer" "region_index" { 36 | max = length(local.azure_regions) - 1 37 | min = 0 38 | } 39 | 40 | # This ensures we have unique CAF compliant names for our resources. 41 | module "naming" { 42 | source = "Azure/naming/azurerm" 43 | version = "0.3.0" 44 | } 45 | 46 | # This is required for resource modules 47 | resource "azurerm_resource_group" "rg" { 48 | location = local.azure_regions[random_integer.region_index.result] 49 | name = module.naming.resource_group.name_unique 50 | } 51 | 52 | module "vnet" { 53 | source = "Azure/avm-res-network-virtualnetwork/azurerm" 54 | version = "0.16.0" 55 | 56 | location = azurerm_resource_group.rg.location 57 | parent_id = azurerm_resource_group.rg.id 58 | address_space = ["10.1.0.0/16"] 59 | enable_telemetry = var.enable_telemetry 60 | name = module.naming.virtual_network.name_unique 61 | } 62 | 63 | resource "azurerm_subnet" "subnet" { 64 | address_prefixes = ["10.1.0.0/26"] 65 | name = "AzureFirewallSubnet" 66 | resource_group_name = azurerm_resource_group.rg.name 67 | virtual_network_name = module.vnet.resource.name 68 | } 69 | 70 | resource "azurerm_public_ip" "pip" { 71 | allocation_method = "Static" 72 | location = azurerm_resource_group.rg.location 73 | name = "pip" 74 | resource_group_name = azurerm_resource_group.rg.name 75 | sku = "Standard" 76 | zones = ["1", "2", "3"] 77 | } 78 | 79 | resource "azurerm_ip_group" "ipgroup_1" { 80 | location = azurerm_resource_group.rg.location 81 | name = "ipgroup1" 82 | resource_group_name = azurerm_resource_group.rg.name 83 | cidrs = ["192.168.0.1", "172.16.240.0/20", "10.48.0.0/12"] 84 | } 85 | 86 | resource "azurerm_ip_group" "ipgroup_2" { 87 | location = azurerm_resource_group.rg.location 88 | name = "ipgroup2" 89 | resource_group_name = azurerm_resource_group.rg.name 90 | cidrs = ["10.100.10.0/24", "192.100.10.4", "10.150.20.20"] 91 | } 92 | 93 | # This is the module call 94 | module "firewall" { 95 | source = "Azure/avm-res-network-azurefirewall/azurerm" 96 | version = "0.4.0" 97 | 98 | firewall_sku_name = "AZFW_VNet" 99 | firewall_sku_tier = "Standard" 100 | location = azurerm_resource_group.rg.location 101 | name = module.naming.firewall.name 102 | resource_group_name = azurerm_resource_group.rg.name 103 | enable_telemetry = var.enable_telemetry 104 | firewall_ip_configuration = [ 105 | { 106 | name = "ipconfig1" 107 | subnet_id = azurerm_subnet.subnet.id 108 | public_ip_address_id = azurerm_public_ip.pip.id 109 | } 110 | ] 111 | firewall_policy_id = module.firewall_policy.resource.id 112 | firewall_zones = ["1", "2", "3"] 113 | } 114 | 115 | module "firewall_policy" { 116 | source = "../.." 117 | 118 | location = azurerm_resource_group.rg.location 119 | name = module.naming.firewall_policy.name 120 | resource_group_name = azurerm_resource_group.rg.name 121 | enable_telemetry = var.enable_telemetry 122 | } 123 | 124 | module "rule_collection_group" { 125 | source = "../../modules/rule_collection_groups" 126 | 127 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 128 | firewall_policy_rule_collection_group_name = "IPGroupRCG" 129 | firewall_policy_rule_collection_group_priority = 400 130 | firewall_policy_rule_collection_group_application_rule_collection = [ 131 | { 132 | action = "Allow" 133 | name = "ApplicationRuleCollection" 134 | priority = 201 135 | rule = [ 136 | { 137 | name = "AllowMicrosoft" 138 | destination_fqdns = ["*.microsoft.com"] 139 | source_ip_groups = [azurerm_ip_group.ipgroup_2.id] 140 | protocols = [ 141 | { 142 | port = 443 143 | type = "Https" 144 | } 145 | ] 146 | } 147 | ] 148 | } 149 | ] 150 | firewall_policy_rule_collection_group_network_rule_collection = [ 151 | { 152 | action = "Allow" 153 | name = "NetworkRuleCollection" 154 | priority = 101 155 | rule = [ 156 | { 157 | name = "OutboundToIPGroups" 158 | destination_ports = ["443"] 159 | destination_ip_groups = [azurerm_ip_group.ipgroup_1.id] 160 | source_ip_groups = [azurerm_ip_group.ipgroup_2.id] 161 | protocols = ["TCP"] 162 | } 163 | ] 164 | } 165 | ] 166 | } 167 | ``` 168 | 169 | 170 | ## Requirements 171 | 172 | The following requirements are needed by this module: 173 | 174 | - [terraform](#requirement\_terraform) (>= 1.3.0) 175 | 176 | - [azurerm](#requirement\_azurerm) (>= 3.71, < 5.0.0) 177 | 178 | - [random](#requirement\_random) (>= 3.5.0, < 4.0.0) 179 | 180 | ## Resources 181 | 182 | The following resources are used by this module: 183 | 184 | - [azurerm_ip_group.ipgroup_1](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/ip_group) (resource) 185 | - [azurerm_ip_group.ipgroup_2](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/ip_group) (resource) 186 | - [azurerm_public_ip.pip](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip) (resource) 187 | - [azurerm_resource_group.rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) (resource) 188 | - [azurerm_subnet.subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) (resource) 189 | - [random_integer.region_index](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) (resource) 190 | 191 | 192 | ## Required Inputs 193 | 194 | No required inputs. 195 | 196 | ## Optional Inputs 197 | 198 | The following input variables are optional (have default values): 199 | 200 | ### [enable\_telemetry](#input\_enable\_telemetry) 201 | 202 | Description: This variable controls whether or not telemetry is enabled for the module. 203 | For more information see https://aka.ms/avm/telemetryinfo. 204 | If it is set to false, then no telemetry will be collected. 205 | 206 | Type: `bool` 207 | 208 | Default: `true` 209 | 210 | ## Outputs 211 | 212 | No outputs. 213 | 214 | ## Modules 215 | 216 | The following Modules are called: 217 | 218 | ### [firewall](#module\_firewall) 219 | 220 | Source: Azure/avm-res-network-azurefirewall/azurerm 221 | 222 | Version: 0.4.0 223 | 224 | ### [firewall\_policy](#module\_firewall\_policy) 225 | 226 | Source: ../.. 227 | 228 | Version: 229 | 230 | ### [naming](#module\_naming) 231 | 232 | Source: Azure/naming/azurerm 233 | 234 | Version: 0.3.0 235 | 236 | ### [rule\_collection\_group](#module\_rule\_collection\_group) 237 | 238 | Source: ../../modules/rule_collection_groups 239 | 240 | Version: 241 | 242 | ### [vnet](#module\_vnet) 243 | 244 | Source: Azure/avm-res-network-virtualnetwork/azurerm 245 | 246 | Version: 0.16.0 247 | 248 | 249 | ## Data Collection 250 | 251 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 252 | -------------------------------------------------------------------------------- /.github/policies/scheduledSearches.yml: -------------------------------------------------------------------------------- 1 | id: avmScheduledSearches 2 | name: AVM Scheduled Searches 3 | description: AVM Scheduled Searches 4 | resource: repository 5 | disabled: false 6 | 7 | configuration: 8 | resourceManagementConfiguration: 9 | scheduledSearches: 10 | - description: "ITA01TF.1 - Label and comment AVM issues that have been marked as requiring triage and have not had any activity for 3 business days." 11 | frequencies: 12 | - weekday: 13 | day: Monday 14 | time: 12:00 15 | - weekday: 16 | day: Tuesday 17 | time: 12:00 18 | - weekday: 19 | day: Wednesday 20 | time: 12:00 21 | filters: 22 | - isIssue 23 | - isOpen 24 | - hasLabel: 25 | label: "Needs: Triage :mag:" 26 | - isNotLabeledWith: 27 | label: "Status: Response Overdue :triangular_flag_on_post:" 28 | - noActivitySince: 29 | days: 5 30 | actions: 31 | - mentionUsers: 32 | mentionees: 33 | - Azure/avm-core-team-technical-terraform 34 | replyTemplate: | 35 | > [!WARNING] 36 | > **Tagging the AVM Core Team (${mentionees}) due to a module owner or contributor having not responded to this issue within 3 business days. The AVM Core Team will attempt to contact the module owners/contributors directly.** 37 | 38 | > [!TIP] 39 | > - To prevent further actions to take effect, the "Status: Response Overdue 🚩" label must be removed, once this issue has been responded to. 40 | > - To avoid this rule being (re)triggered, the ""Needs: Triage :mag:" label must be removed as part of the triage process (when the issue is first responded to)! 41 | - addLabel: 42 | label: "Status: Response Overdue :triangular_flag_on_post:" 43 | 44 | - description: "ITA01TF.2 - Label and comment AVM issues that have been marked as requiring triage and have not had any activity for 3 business days." 45 | frequencies: 46 | - weekday: 47 | day: Thursday 48 | time: 12:00 49 | - weekday: 50 | day: Friday 51 | time: 12:00 52 | filters: 53 | - isIssue 54 | - isOpen 55 | - hasLabel: 56 | label: "Needs: Triage :mag:" 57 | - isNotLabeledWith: 58 | label: "Status: Response Overdue :triangular_flag_on_post:" 59 | - noActivitySince: 60 | days: 3 61 | actions: 62 | - mentionUsers: 63 | mentionees: 64 | - Azure/avm-core-team-technical-terraform 65 | replyTemplate: | 66 | > [!WARNING] 67 | > **Tagging the AVM Core Team (${mentionees}) due to a module owner or contributor having not responded to this issue within 3 business days. The AVM Core Team will attempt to contact the module owners/contributors directly.** 68 | 69 | > [!TIP] 70 | > - To prevent further actions to take effect, the "Status: Response Overdue 🚩" label must be removed, once this issue has been responded to. 71 | > - To avoid this rule being (re)triggered, the ""Needs: Triage :mag:" label must be removed as part of the triage process (when the issue is first responded to)! 72 | - addLabel: 73 | label: "Status: Response Overdue :triangular_flag_on_post:" 74 | - assignTo: 75 | user: Azure/avm-core-team-technical-terraform 76 | 77 | - description: "ITA02TF.1 - Label and comment issues as Needs Immediate Attention and leave comment if after an additional 3 business days there's still no update to the issue." 78 | frequencies: 79 | - weekday: 80 | day: Monday 81 | time: 12:00 82 | - weekday: 83 | day: Tuesday 84 | time: 12:00 85 | - weekday: 86 | day: Wednesday 87 | time: 12:00 88 | filters: 89 | - isIssue 90 | - isOpen 91 | - hasLabel: 92 | label: "Status: Response Overdue :triangular_flag_on_post:" 93 | - isNotLabeledWith: 94 | label: "Needs: Immediate Attention :bangbang:" 95 | - noActivitySince: 96 | days: 5 97 | actions: 98 | - mentionUsers: 99 | mentionees: 100 | - Azure/avm-core-team-technical-terraform 101 | replyTemplate: | 102 | > [!CAUTION] 103 | > **This issue requires the AVM Core Team's (${mentionees}) immediate attention as it hasn't been responded to within 6 business days. ** 104 | 105 | > [!TIP] 106 | > - To avoid this rule being (re)triggered, the "Needs: Triage :mag:" and "Status: Response Overdue :triangular_flag_on_post:" labels must be removed when the issue is first responded to! 107 | > - Remove the "Needs: Immediate Attention :bangbang:" label once the issue has been responded to. 108 | - addLabel: 109 | label: "Needs: Immediate Attention :bangbang:" 110 | 111 | - description: "ITA02TF.2 - Label and comment issues as Needs Immediate Attention and leave comment if after an additional 3 business days there's still no update to the issue." 112 | frequencies: 113 | - weekday: 114 | day: Thursday 115 | time: 12:00 116 | - weekday: 117 | day: Friday 118 | time: 12:00 119 | filters: 120 | - isIssue 121 | - isOpen 122 | - hasLabel: 123 | label: "Status: Response Overdue :triangular_flag_on_post:" 124 | - isNotLabeledWith: 125 | label: "Needs: Immediate Attention :bangbang:" 126 | - noActivitySince: 127 | days: 3 128 | actions: 129 | - mentionUsers: 130 | mentionees: 131 | - Azure/avm-core-team-technical-terraform 132 | replyTemplate: | 133 | > [!CAUTION] 134 | > **This issue requires the AVM Core Team's (${mentionees}) immediate attention as it hasn't been responded to within 6 business days.** 135 | 136 | > [!TIP] 137 | > - To avoid this rule being (re)triggered, the "Needs: Triage :mag:" and "Status: Response Overdue :triangular_flag_on_post:" labels must be removed when the issue is first responded to! 138 | > - Remove the "Needs: Immediate Attention :bangbang:" label once the issue has been responded to. 139 | - addLabel: 140 | label: "Needs: Immediate Attention :bangbang:" 141 | 142 | - description: "ITA03TF - Label and mention Terraform PG to security issues that have not had any activity for 5 business days." 143 | frequencies: 144 | - weekday: 145 | day: Monday 146 | time: 12:00 147 | - weekday: 148 | day: Tuesday 149 | time: 12:00 150 | - weekday: 151 | day: Wednesday 152 | time: 12:00 153 | - weekday: 154 | day: Thursday 155 | time: 12:00 156 | - weekday: 157 | day: Friday 158 | time: 12:00 159 | filters: 160 | - isIssue 161 | - isOpen 162 | - hasLabel: 163 | label: "Needs: Triage :mag:" 164 | - hasLabel: 165 | label: "Type: Security Bug :lock:" 166 | - hasLabel: 167 | label: "Status: Response Overdue :triangular_flag_on_post:" 168 | - noActivitySince: 169 | days: 7 170 | actions: 171 | - mentionUsers: 172 | mentionees: 173 | - Azure/terraform-avm 174 | replyTemplate: | 175 | > [!CAUTION] 176 | > **Tagging the Terraform PG team (${mentionees}) due to a module owner or contributor having not responded to this issue within 3 business days. The AVM Core Team will attempt to contact the module owners/contributors directly.** 177 | 178 | > [!TIP] 179 | > - To avoid this rule being (re)triggered, the "Needs: Triage :mag:" and "Status: Response Overdue :triangular_flag_on_post:" labels must be removed when the issue is first responded to! 180 | > - Remove the "Needs: Immediate Attention :bangbang:" label once the issue has been responded to. 181 | - addLabel: 182 | label: "Needs: Immediate Attention :bangbang:" 183 | - assignTo: 184 | user: Azure/terraform-avm 185 | 186 | - description: "ITA04 - Label issues and PRs that have been marked as requiring author feedback but have not had any activity for 4 days." 187 | frequencies: 188 | - hourly: 189 | hour: 3 190 | filters: 191 | - isOpen 192 | - hasLabel: 193 | label: "Needs: Author Feedback :ear:" 194 | - noActivitySince: 195 | days: 4 196 | - isNotLabeledWith: 197 | label: "Status: No Recent Activity :zzz:" 198 | actions: 199 | - addLabel: 200 | label: "Status: No Recent Activity :zzz:" 201 | - addReply: 202 | reply: | 203 | - addReply: 204 | reply: | 205 | > [!IMPORTANT] 206 | > @${issueAuthor}, this issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **4 days**. 207 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_for_avd/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.3.0" 3 | 4 | required_providers { 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = ">= 3.71, < 5.0.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = ">= 3.5.0, < 4.0.0" 12 | } 13 | } 14 | } 15 | 16 | provider "azurerm" { 17 | features {} 18 | } 19 | 20 | # This picks a random region from the list of regions. 21 | resource "random_integer" "region_index" { 22 | max = length(local.azure_regions) - 1 23 | min = 0 24 | } 25 | 26 | # This ensures we have unique CAF compliant names for our resources. 27 | module "naming" { 28 | source = "Azure/naming/azurerm" 29 | version = "0.3.0" 30 | } 31 | 32 | # This is required for resource modules 33 | resource "azurerm_resource_group" "this" { 34 | location = local.azure_regions[random_integer.region_index.result] 35 | name = module.naming.resource_group.name_unique 36 | } 37 | 38 | # This is the module call 39 | module "firewall_policy" { 40 | source = "../.." 41 | 42 | location = azurerm_resource_group.this.location 43 | name = module.naming.firewall_policy.name_unique 44 | resource_group_name = azurerm_resource_group.this.name 45 | # source = "Azure/avm-res-network-firewallpolicy/azurerm" 46 | enable_telemetry = var.enable_telemetry 47 | firewall_policy_dns = { 48 | proxy_enabled = true 49 | } 50 | } 51 | 52 | module "avd_core_rule_collection_group" { 53 | source = "../../modules/rule_collection_groups" 54 | 55 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 56 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 57 | firewall_policy_rule_collection_group_name = "NetworkRuleCollectionGroup" 58 | firewall_policy_rule_collection_group_priority = 1000 59 | firewall_policy_rule_collection_group_network_rule_collection = [{ 60 | action = "Allow" 61 | name = "AVDCoreNetworkRules" 62 | priority = 500 63 | rule = [ 64 | { 65 | name = "Login to Microsoft" 66 | source_addresses = ["10.100.0.0/24"] 67 | destination_fqdns = ["login.microsoftonline.com"] 68 | protocols = ["TCP"] 69 | destination_ports = ["443"] 70 | }, 71 | { 72 | name = "AVD" 73 | source_addresses = ["10.100.0.0/24"] 74 | destination_addresses = ["WindowsVirtualDesktop", "AzureFrontDoor.Frontend", "AzureMonitor"] 75 | protocols = ["TCP"] 76 | destination_ports = ["443"] 77 | }, 78 | { 79 | name = "GCS" 80 | source_addresses = ["10.100.0.0/24"] 81 | destination_fqdns = ["gcs.prod.monitoring.core.windows.net"] 82 | protocols = ["TCP"] 83 | destination_ports = ["443"] 84 | }, 85 | { 86 | name = "DNS" 87 | source_addresses = ["10.100.0.0/24"] 88 | destination_addresses = ["AzureDNS"] 89 | protocols = ["TCP", "UDP"] 90 | destination_ports = ["53"] 91 | }, 92 | { 93 | name = "azkms" 94 | source_addresses = ["10.100.0.0/24"] 95 | destination_fqdns = ["azkms.core.windows.net"] 96 | protocols = ["TCP"] 97 | destination_ports = ["1688"] 98 | }, 99 | { 100 | name = "KMS" 101 | source_addresses = ["10.100.0.0/24"] 102 | destination_fqdns = ["kms.core.windows.net"] 103 | protocols = ["TCP"] 104 | destination_ports = ["1688"] 105 | }, 106 | { 107 | name = "mrglobalblob" 108 | source_addresses = ["10.100.0.0/24"] 109 | destination_fqdns = ["mrsglobalsteus2prod.blob.core.windows.net"] 110 | protocols = ["TCP"] 111 | destination_ports = ["443"] 112 | }, 113 | { 114 | name = "wvdportalstorageblob" 115 | source_addresses = ["10.100.0.0/24"] 116 | destination_fqdns = ["wvdportalstorageblob.blob.core.windows.net"] 117 | protocols = ["TCP"] 118 | destination_ports = ["443"] 119 | }, 120 | { 121 | name = "oneocsp" 122 | source_addresses = ["10.100.0.0/24"] 123 | destination_fqdns = ["oneocsp.microsoft.com"] 124 | protocols = ["TCP"] 125 | destination_ports = ["443"] 126 | }, 127 | { 128 | name = "microsoft.com" 129 | source_addresses = ["10.100.0.0/24"] 130 | destination_fqdns = ["www.microsoft.com"] 131 | protocols = ["TCP"] 132 | destination_ports = ["443"] 133 | }, 134 | ] 135 | } 136 | ] 137 | } 138 | 139 | 140 | module "avd_optional_rule_collection_group" { 141 | source = "../../modules/rule_collection_groups" 142 | 143 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 144 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 145 | firewall_policy_rule_collection_group_name = "AVDOptionalRuleCollectionGroup" 146 | firewall_policy_rule_collection_group_priority = 1050 147 | firewall_policy_rule_collection_group_application_rule_collection = [{ 148 | action = "Allow" 149 | name = "AVDOptionalApplicationRules" 150 | priority = 600 151 | rule = [ 152 | { 153 | name = "Windows" 154 | source_addresses = ["10.0.0.0/24"] 155 | destination_fqdn_tags = ["WindowsUpdate", "WindowsDiagnostics", "MicrosoftActiveProtectionService"] 156 | protocols = [ 157 | { 158 | port = 443 159 | type = "Https" 160 | } 161 | ] 162 | }, 163 | { 164 | name = "Events" 165 | source_addresses = ["10.0.0.0/24"] 166 | destination_fqdns = ["*.events.data.microsoft.com"] 167 | protocols = [ 168 | { 169 | port = 443 170 | type = "Https" 171 | } 172 | ] 173 | }, 174 | { 175 | name = "sfx" 176 | source_addresses = ["10.0.0.0/24"] 177 | destination_fqdns = ["*.sfx.ms"] 178 | protocols = [ 179 | { 180 | port = 443 181 | type = "Https" 182 | } 183 | ] 184 | }, 185 | { 186 | name = "digicert" 187 | source_addresses = ["10.0.0.0/24"] 188 | destination_fqdns = ["*.digicert.com"] 189 | protocols = [ 190 | { 191 | port = 443 192 | type = "Https" 193 | } 194 | ] 195 | }, 196 | { 197 | name = "Azure DNS" 198 | source_addresses = ["10.0.0.0/24"] 199 | destination_fqdns = ["*.azure-dns.com", "*.azure-dns.net"] 200 | protocols = [ 201 | { 202 | port = 443 203 | type = "Https" 204 | } 205 | ] 206 | }, 207 | ] 208 | } 209 | ] 210 | firewall_policy_rule_collection_group_network_rule_collection = [{ 211 | action = "Allow" 212 | name = "AVDOptionalNetworkRules" 213 | priority = 500 214 | rule = [ 215 | { 216 | name = "time" 217 | source_addresses = ["10.0.0.0/24"] 218 | destination_fqdns = ["time.windows.com"] 219 | protocols = ["UDP"] 220 | destination_ports = ["123"] 221 | }, 222 | { 223 | name = "login windows.net" 224 | source_addresses = ["10.0.0.0/24"] 225 | destination_fqdns = ["login.windows.net"] 226 | protocols = ["TCP"] 227 | destination_ports = ["443"] 228 | }, 229 | { 230 | name = "msftconnecttest" 231 | source_addresses = ["10.0.0.0/24"] 232 | destination_fqdns = ["www.msftconnecttest.com"] 233 | protocols = ["TCP"] 234 | destination_ports = ["443"] 235 | }, 236 | ] 237 | } 238 | ] 239 | } 240 | 241 | module "m365rulecollectiongroup" { 242 | source = "../../modules/rule_collection_groups" 243 | 244 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 245 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 246 | firewall_policy_rule_collection_group_name = "M365RuleCollectionGroup" 247 | firewall_policy_rule_collection_group_priority = 2000 248 | firewall_policy_rule_collection_group_network_rule_collection = [{ 249 | action = "Allow" 250 | name = "M365NetworkRules" 251 | priority = 500 252 | rule = [ 253 | { 254 | name = "M365" 255 | source_addresses = ["10.0.0.0/24"] 256 | destination_addresses = ["Office365.Common.Allow.Required"] 257 | protocols = ["TCP"] 258 | destination_ports = ["443"] 259 | } 260 | ] 261 | } 262 | ] 263 | } 264 | 265 | module "internetrulecollectiongroup" { 266 | source = "../../modules/rule_collection_groups" 267 | 268 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 269 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 270 | firewall_policy_rule_collection_group_name = "InternetRuleCollectionGroup" 271 | firewall_policy_rule_collection_group_priority = 3000 272 | firewall_policy_rule_collection_group_network_rule_collection = [{ 273 | action = "Allow" 274 | name = "InternetNetworkRules" 275 | priority = 500 276 | rule = [ 277 | { 278 | name = "Internet" 279 | source_addresses = ["10.0.0.0/24"] 280 | destination_addresses = ["*"] 281 | protocols = ["TCP"] 282 | destination_ports = ["443", "80"] 283 | } 284 | ] 285 | } 286 | ] 287 | } -------------------------------------------------------------------------------- /modules/rule_collection_groups/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Azure Firewall Policy Rule Collection Group 4 | 5 | This is the sub-module to create Rule Collection Groups in Azure Firewall Policy 6 | 7 | ## Features 8 | 9 | This module supports: 10 | 11 | - Creates Rule Collection Groups 12 | - Creates Rule Collections 13 | - Creates Network Rules, Application Rules, and NAT Rules 14 | 15 | "Major version Zero (0.y.z) is for initial development. Anything MAY change at any time. The module SHOULD NOT be considered stable till at least it is major version one (1.0.0) or greater. Changes will always be via new versions being published and no changes will be made to existing published versions. For more details please go to " 16 | 17 | 18 | ## Requirements 19 | 20 | The following requirements are needed by this module: 21 | 22 | - [terraform](#requirement\_terraform) (~> 1.5) 23 | 24 | - [azurerm](#requirement\_azurerm) (>= 3.71, < 5.0.0) 25 | 26 | ## Resources 27 | 28 | The following resources are used by this module: 29 | 30 | - [azurerm_firewall_policy_rule_collection_group.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/firewall_policy_rule_collection_group) (resource) 31 | 32 | 33 | ## Required Inputs 34 | 35 | The following input variables are required: 36 | 37 | ### [firewall\_policy\_rule\_collection\_group\_firewall\_policy\_id](#input\_firewall\_policy\_rule\_collection\_group\_firewall\_policy\_id) 38 | 39 | Description: (Required) The ID of the Firewall Policy where the Firewall Policy Rule Collection Group should exist. Changing this forces a new Firewall Policy Rule Collection Group to be created. 40 | 41 | Type: `string` 42 | 43 | ### [firewall\_policy\_rule\_collection\_group\_name](#input\_firewall\_policy\_rule\_collection\_group\_name) 44 | 45 | Description: (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 46 | 47 | Type: `string` 48 | 49 | ### [firewall\_policy\_rule\_collection\_group\_priority](#input\_firewall\_policy\_rule\_collection\_group\_priority) 50 | 51 | Description: (Required) The priority of the Firewall Policy Rule Collection Group. The range is 100-65000. 52 | 53 | Type: `number` 54 | 55 | ## Optional Inputs 56 | 57 | The following input variables are optional (have default values): 58 | 59 | ### [firewall\_policy\_rule\_collection\_group\_application\_rule\_collection](#input\_firewall\_policy\_rule\_collection\_group\_application\_rule\_collection) 60 | 61 | Description: - `action` - (Required) The action to take for the application rules in this collection. Possible values are `Allow` and `Deny`. 62 | - `name` - (Required) The name which should be used for this application rule collection. 63 | - `priority` - (Required) The priority of the application rule collection. The range is `100` 64 | 65 | --- 66 | `rule` block supports the following: 67 | - `description` - 68 | - `destination_addresses` - 69 | - `destination_fqdn_tags` - 70 | - `destination_fqdns` - 71 | - `destination_urls` - 72 | - `name` - (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 73 | - `source_addresses` - 74 | - `source_ip_groups` - 75 | - `terminate_tls` - 76 | - `web_categories` - 77 | 78 | --- 79 | `http_headers` block supports the following: 80 | - `name` - (Required) Specifies the name of the header. 81 | - `value` - (Required) Specifies the value of the value. 82 | 83 | --- 84 | `protocols` block supports the following: 85 | - `port` - (Required) Port number of the protocol. Range is 0-64000. 86 | - `type` - (Required) Protocol type. Possible values are `Http` and `Https`. 87 | 88 | Type: 89 | 90 | ```hcl 91 | list(object({ 92 | action = string 93 | name = string 94 | priority = number 95 | rule = list(object({ 96 | description = optional(string) 97 | destination_addresses = optional(list(string), []) 98 | destination_fqdn_tags = optional(list(string), []) 99 | destination_fqdns = optional(list(string), []) 100 | destination_urls = optional(list(string), []) 101 | name = string 102 | source_addresses = optional(list(string), []) 103 | source_ip_groups = optional(list(string), []) 104 | terminate_tls = optional(bool) 105 | web_categories = optional(list(string), []) 106 | http_headers = optional(list(object({ 107 | name = string 108 | value = string 109 | }))) 110 | protocols = optional(list(object({ 111 | port = number 112 | type = string 113 | }))) 114 | })) 115 | })) 116 | ``` 117 | 118 | Default: `null` 119 | 120 | ### [firewall\_policy\_rule\_collection\_group\_nat\_rule\_collection](#input\_firewall\_policy\_rule\_collection\_group\_nat\_rule\_collection) 121 | 122 | Description: - `action` - (Required) The action to take for the NAT rules in this collection. Currently, the only possible value is `Dnat`. 123 | - `name` - (Required) The name which should be used for this NAT rule collection. 124 | - `priority` - (Required) The priority of the NAT rule collection. The range is `100` 125 | 126 | --- 127 | `rule` block supports the following: 128 | - `description` - 129 | - `destination_address` - 130 | - `destination_ports` - 131 | - `name` - (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 132 | - `protocols` - 133 | - `source_addresses` - 134 | - `source_ip_groups` - 135 | - `translated_address` - 136 | - `translated_fqdn` - 137 | - `translated_port` - 138 | 139 | Type: 140 | 141 | ```hcl 142 | list(object({ 143 | action = string 144 | name = string 145 | priority = number 146 | rule = list(object({ 147 | description = optional(string) 148 | destination_address = optional(string) 149 | destination_ports = optional(list(string), []) 150 | name = string 151 | protocols = list(string) 152 | source_addresses = optional(list(string), []) 153 | source_ip_groups = optional(list(string), []) 154 | translated_address = optional(string) 155 | translated_fqdn = optional(string) 156 | translated_port = number 157 | })) 158 | })) 159 | ``` 160 | 161 | Default: `null` 162 | 163 | ### [firewall\_policy\_rule\_collection\_group\_network\_rule\_collection](#input\_firewall\_policy\_rule\_collection\_group\_network\_rule\_collection) 164 | 165 | Description: - `action` - (Required) The action to take for the network rules in this collection. Possible values are `Allow` and `Deny`. 166 | - `name` - (Required) The name which should be used for this network rule collection. 167 | - `priority` - (Required) The priority of the network rule collection. The range is `100` 168 | 169 | --- 170 | `rule` block supports the following: 171 | - `description` - 172 | - `destination_addresses` - 173 | - `destination_fqdns` - 174 | - `destination_ip_groups` - 175 | - `destination_ports` - 176 | - `name` - (Required) The name which should be used for this Firewall Policy Rule Collection Group. Changing this forces a new Firewall Policy Rule Collection Group to be created. 177 | - `protocols` - 178 | - `source_addresses` - 179 | - `source_ip_groups` - 180 | 181 | Type: 182 | 183 | ```hcl 184 | list(object({ 185 | action = string 186 | name = string 187 | priority = number 188 | rule = list(object({ 189 | description = optional(string) 190 | destination_addresses = optional(list(string), []) 191 | destination_fqdns = optional(list(string), []) 192 | destination_ip_groups = optional(list(string), []) 193 | destination_ports = list(string) 194 | name = string 195 | protocols = list(string) 196 | source_addresses = optional(list(string), []) 197 | source_ip_groups = optional(list(string), []) 198 | })) 199 | })) 200 | ``` 201 | 202 | Default: `null` 203 | 204 | ### [firewall\_policy\_rule\_collection\_group\_timeouts](#input\_firewall\_policy\_rule\_collection\_group\_timeouts) 205 | 206 | Description: - `create` - (Defaults to 30 minutes) Used when creating the Firewall Policy Rule Collection Group. 207 | - `delete` - (Defaults to 30 minutes) Used when deleting the Firewall Policy Rule Collection Group. 208 | - `read` - (Defaults to 5 minutes) Used when retrieving the Firewall Policy Rule Collection Group. 209 | - `update` - (Defaults to 30 minutes) Used when updating the Firewall Policy Rule Collection Group. 210 | 211 | Type: 212 | 213 | ```hcl 214 | object({ 215 | create = optional(string) 216 | delete = optional(string) 217 | read = optional(string) 218 | update = optional(string) 219 | }) 220 | ``` 221 | 222 | Default: `null` 223 | 224 | ## Outputs 225 | 226 | The following outputs are exported: 227 | 228 | ### [resource](#output\_resource) 229 | 230 | Description: this is the resource of the rule collection group 231 | 232 | ### [resource\_id](#output\_resource\_id) 233 | 234 | Description: the resource id of the rule\_collection\_group 235 | 236 | ## Modules 237 | 238 | No modules. 239 | 240 | 241 | ## Data Collection 242 | 243 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 244 | -------------------------------------------------------------------------------- /examples/deploy_fw_policy_for_avd/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Deploy Firewall Policy for Azure Virtual Desktop 4 | 5 | This example deploys an Azure Firewall Policy with the required rules needed for Azure Virtual Desktop. 6 | 7 | - Firewall Policy 8 | - Rule Collection Groups 9 | - Rule Collections 10 | - Network and Application Rules 11 | 12 | ```hcl 13 | terraform { 14 | required_version = ">= 1.3.0" 15 | 16 | required_providers { 17 | azurerm = { 18 | source = "hashicorp/azurerm" 19 | version = ">= 3.71, < 5.0.0" 20 | } 21 | random = { 22 | source = "hashicorp/random" 23 | version = ">= 3.5.0, < 4.0.0" 24 | } 25 | } 26 | } 27 | 28 | provider "azurerm" { 29 | features {} 30 | } 31 | 32 | # This picks a random region from the list of regions. 33 | resource "random_integer" "region_index" { 34 | max = length(local.azure_regions) - 1 35 | min = 0 36 | } 37 | 38 | # This ensures we have unique CAF compliant names for our resources. 39 | module "naming" { 40 | source = "Azure/naming/azurerm" 41 | version = "0.3.0" 42 | } 43 | 44 | # This is required for resource modules 45 | resource "azurerm_resource_group" "this" { 46 | location = local.azure_regions[random_integer.region_index.result] 47 | name = module.naming.resource_group.name_unique 48 | } 49 | 50 | # This is the module call 51 | module "firewall_policy" { 52 | source = "../.." 53 | 54 | location = azurerm_resource_group.this.location 55 | name = module.naming.firewall_policy.name_unique 56 | resource_group_name = azurerm_resource_group.this.name 57 | # source = "Azure/avm-res-network-firewallpolicy/azurerm" 58 | enable_telemetry = var.enable_telemetry 59 | firewall_policy_dns = { 60 | proxy_enabled = true 61 | } 62 | } 63 | 64 | module "avd_core_rule_collection_group" { 65 | source = "../../modules/rule_collection_groups" 66 | 67 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 68 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 69 | firewall_policy_rule_collection_group_name = "NetworkRuleCollectionGroup" 70 | firewall_policy_rule_collection_group_priority = 1000 71 | firewall_policy_rule_collection_group_network_rule_collection = [{ 72 | action = "Allow" 73 | name = "AVDCoreNetworkRules" 74 | priority = 500 75 | rule = [ 76 | { 77 | name = "Login to Microsoft" 78 | source_addresses = ["10.100.0.0/24"] 79 | destination_fqdns = ["login.microsoftonline.com"] 80 | protocols = ["TCP"] 81 | destination_ports = ["443"] 82 | }, 83 | { 84 | name = "AVD" 85 | source_addresses = ["10.100.0.0/24"] 86 | destination_addresses = ["WindowsVirtualDesktop", "AzureFrontDoor.Frontend", "AzureMonitor"] 87 | protocols = ["TCP"] 88 | destination_ports = ["443"] 89 | }, 90 | { 91 | name = "GCS" 92 | source_addresses = ["10.100.0.0/24"] 93 | destination_fqdns = ["gcs.prod.monitoring.core.windows.net"] 94 | protocols = ["TCP"] 95 | destination_ports = ["443"] 96 | }, 97 | { 98 | name = "DNS" 99 | source_addresses = ["10.100.0.0/24"] 100 | destination_addresses = ["AzureDNS"] 101 | protocols = ["TCP", "UDP"] 102 | destination_ports = ["53"] 103 | }, 104 | { 105 | name = "azkms" 106 | source_addresses = ["10.100.0.0/24"] 107 | destination_fqdns = ["azkms.core.windows.net"] 108 | protocols = ["TCP"] 109 | destination_ports = ["1688"] 110 | }, 111 | { 112 | name = "KMS" 113 | source_addresses = ["10.100.0.0/24"] 114 | destination_fqdns = ["kms.core.windows.net"] 115 | protocols = ["TCP"] 116 | destination_ports = ["1688"] 117 | }, 118 | { 119 | name = "mrglobalblob" 120 | source_addresses = ["10.100.0.0/24"] 121 | destination_fqdns = ["mrsglobalsteus2prod.blob.core.windows.net"] 122 | protocols = ["TCP"] 123 | destination_ports = ["443"] 124 | }, 125 | { 126 | name = "wvdportalstorageblob" 127 | source_addresses = ["10.100.0.0/24"] 128 | destination_fqdns = ["wvdportalstorageblob.blob.core.windows.net"] 129 | protocols = ["TCP"] 130 | destination_ports = ["443"] 131 | }, 132 | { 133 | name = "oneocsp" 134 | source_addresses = ["10.100.0.0/24"] 135 | destination_fqdns = ["oneocsp.microsoft.com"] 136 | protocols = ["TCP"] 137 | destination_ports = ["443"] 138 | }, 139 | { 140 | name = "microsoft.com" 141 | source_addresses = ["10.100.0.0/24"] 142 | destination_fqdns = ["www.microsoft.com"] 143 | protocols = ["TCP"] 144 | destination_ports = ["443"] 145 | }, 146 | ] 147 | } 148 | ] 149 | } 150 | 151 | 152 | module "avd_optional_rule_collection_group" { 153 | source = "../../modules/rule_collection_groups" 154 | 155 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 156 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 157 | firewall_policy_rule_collection_group_name = "AVDOptionalRuleCollectionGroup" 158 | firewall_policy_rule_collection_group_priority = 1050 159 | firewall_policy_rule_collection_group_application_rule_collection = [{ 160 | action = "Allow" 161 | name = "AVDOptionalApplicationRules" 162 | priority = 600 163 | rule = [ 164 | { 165 | name = "Windows" 166 | source_addresses = ["10.0.0.0/24"] 167 | destination_fqdn_tags = ["WindowsUpdate", "WindowsDiagnostics", "MicrosoftActiveProtectionService"] 168 | protocols = [ 169 | { 170 | port = 443 171 | type = "Https" 172 | } 173 | ] 174 | }, 175 | { 176 | name = "Events" 177 | source_addresses = ["10.0.0.0/24"] 178 | destination_fqdns = ["*.events.data.microsoft.com"] 179 | protocols = [ 180 | { 181 | port = 443 182 | type = "Https" 183 | } 184 | ] 185 | }, 186 | { 187 | name = "sfx" 188 | source_addresses = ["10.0.0.0/24"] 189 | destination_fqdns = ["*.sfx.ms"] 190 | protocols = [ 191 | { 192 | port = 443 193 | type = "Https" 194 | } 195 | ] 196 | }, 197 | { 198 | name = "digicert" 199 | source_addresses = ["10.0.0.0/24"] 200 | destination_fqdns = ["*.digicert.com"] 201 | protocols = [ 202 | { 203 | port = 443 204 | type = "Https" 205 | } 206 | ] 207 | }, 208 | { 209 | name = "Azure DNS" 210 | source_addresses = ["10.0.0.0/24"] 211 | destination_fqdns = ["*.azure-dns.com", "*.azure-dns.net"] 212 | protocols = [ 213 | { 214 | port = 443 215 | type = "Https" 216 | } 217 | ] 218 | }, 219 | ] 220 | } 221 | ] 222 | firewall_policy_rule_collection_group_network_rule_collection = [{ 223 | action = "Allow" 224 | name = "AVDOptionalNetworkRules" 225 | priority = 500 226 | rule = [ 227 | { 228 | name = "time" 229 | source_addresses = ["10.0.0.0/24"] 230 | destination_fqdns = ["time.windows.com"] 231 | protocols = ["UDP"] 232 | destination_ports = ["123"] 233 | }, 234 | { 235 | name = "login windows.net" 236 | source_addresses = ["10.0.0.0/24"] 237 | destination_fqdns = ["login.windows.net"] 238 | protocols = ["TCP"] 239 | destination_ports = ["443"] 240 | }, 241 | { 242 | name = "msftconnecttest" 243 | source_addresses = ["10.0.0.0/24"] 244 | destination_fqdns = ["www.msftconnecttest.com"] 245 | protocols = ["TCP"] 246 | destination_ports = ["443"] 247 | }, 248 | ] 249 | } 250 | ] 251 | } 252 | 253 | module "m365rulecollectiongroup" { 254 | source = "../../modules/rule_collection_groups" 255 | 256 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 257 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 258 | firewall_policy_rule_collection_group_name = "M365RuleCollectionGroup" 259 | firewall_policy_rule_collection_group_priority = 2000 260 | firewall_policy_rule_collection_group_network_rule_collection = [{ 261 | action = "Allow" 262 | name = "M365NetworkRules" 263 | priority = 500 264 | rule = [ 265 | { 266 | name = "M365" 267 | source_addresses = ["10.0.0.0/24"] 268 | destination_addresses = ["Office365.Common.Allow.Required"] 269 | protocols = ["TCP"] 270 | destination_ports = ["443"] 271 | } 272 | ] 273 | } 274 | ] 275 | } 276 | 277 | module "internetrulecollectiongroup" { 278 | source = "../../modules/rule_collection_groups" 279 | 280 | # source = "Azure/avm-res-network-firewallpolicy/azurerm//modules/rule_collection_groups" 281 | firewall_policy_rule_collection_group_firewall_policy_id = module.firewall_policy.resource.id 282 | firewall_policy_rule_collection_group_name = "InternetRuleCollectionGroup" 283 | firewall_policy_rule_collection_group_priority = 3000 284 | firewall_policy_rule_collection_group_network_rule_collection = [{ 285 | action = "Allow" 286 | name = "InternetNetworkRules" 287 | priority = 500 288 | rule = [ 289 | { 290 | name = "Internet" 291 | source_addresses = ["10.0.0.0/24"] 292 | destination_addresses = ["*"] 293 | protocols = ["TCP"] 294 | destination_ports = ["443", "80"] 295 | } 296 | ] 297 | } 298 | ] 299 | } 300 | ``` 301 | 302 | 303 | ## Requirements 304 | 305 | The following requirements are needed by this module: 306 | 307 | - [terraform](#requirement\_terraform) (>= 1.3.0) 308 | 309 | - [azurerm](#requirement\_azurerm) (>= 3.71, < 5.0.0) 310 | 311 | - [random](#requirement\_random) (>= 3.5.0, < 4.0.0) 312 | 313 | ## Resources 314 | 315 | The following resources are used by this module: 316 | 317 | - [azurerm_resource_group.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) (resource) 318 | - [random_integer.region_index](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) (resource) 319 | 320 | 321 | ## Required Inputs 322 | 323 | No required inputs. 324 | 325 | ## Optional Inputs 326 | 327 | The following input variables are optional (have default values): 328 | 329 | ### [enable\_telemetry](#input\_enable\_telemetry) 330 | 331 | Description: This variable controls whether or not telemetry is enabled for the module. 332 | For more information see https://aka.ms/avm/telemetryinfo. 333 | If it is set to false, then no telemetry will be collected. 334 | 335 | Type: `bool` 336 | 337 | Default: `true` 338 | 339 | ## Outputs 340 | 341 | No outputs. 342 | 343 | ## Modules 344 | 345 | The following Modules are called: 346 | 347 | ### [avd\_core\_rule\_collection\_group](#module\_avd\_core\_rule\_collection\_group) 348 | 349 | Source: ../../modules/rule_collection_groups 350 | 351 | Version: 352 | 353 | ### [avd\_optional\_rule\_collection\_group](#module\_avd\_optional\_rule\_collection\_group) 354 | 355 | Source: ../../modules/rule_collection_groups 356 | 357 | Version: 358 | 359 | ### [firewall\_policy](#module\_firewall\_policy) 360 | 361 | Source: ../.. 362 | 363 | Version: 364 | 365 | ### [internetrulecollectiongroup](#module\_internetrulecollectiongroup) 366 | 367 | Source: ../../modules/rule_collection_groups 368 | 369 | Version: 370 | 371 | ### [m365rulecollectiongroup](#module\_m365rulecollectiongroup) 372 | 373 | Source: ../../modules/rule_collection_groups 374 | 375 | Version: 376 | 377 | ### [naming](#module\_naming) 378 | 379 | Source: Azure/naming/azurerm 380 | 381 | Version: 0.3.0 382 | 383 | 384 | ## Data Collection 385 | 386 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 387 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "location" { 2 | type = string 3 | description = "(Required) The Azure Region where the Firewall Policy should exist. Changing this forces a new Firewall Policy to be created." 4 | nullable = false 5 | } 6 | 7 | variable "name" { 8 | type = string 9 | description = "(Required) The name which should be used for this Firewall Policy. Changing this forces a new Firewall Policy to be created." 10 | nullable = false 11 | } 12 | 13 | variable "resource_group_name" { 14 | type = string 15 | description = "(Required) The name of the Resource Group where the Firewall Policy should exist. Changing this forces a new Firewall Policy to be created." 16 | nullable = false 17 | } 18 | 19 | variable "diagnostic_settings" { 20 | type = map(object({ 21 | name = optional(string, null) 22 | log_categories = optional(set(string), []) 23 | log_groups = optional(set(string), ["allLogs"]) 24 | metric_categories = optional(set(string), ["AllMetrics"]) 25 | log_analytics_destination_type = optional(string, "Dedicated") 26 | workspace_resource_id = optional(string, null) 27 | storage_account_resource_id = optional(string, null) 28 | event_hub_authorization_rule_resource_id = optional(string, null) 29 | event_hub_name = optional(string, null) 30 | marketplace_partner_resource_id = optional(string, null) 31 | })) 32 | default = {} 33 | description = <. 70 | If it is set to false, then no telemetry will be collected. 71 | DESCRIPTION 72 | nullable = false 73 | } 74 | 75 | variable "firewall_policy_auto_learn_private_ranges_enabled" { 76 | type = bool 77 | default = null 78 | description = "(Optional) Whether enable auto learn private ip range." 79 | } 80 | 81 | variable "firewall_policy_base_policy_id" { 82 | type = string 83 | default = null 84 | description = "(Optional) The ID of the base Firewall Policy." 85 | } 86 | 87 | variable "firewall_policy_dns" { 88 | type = object({ 89 | proxy_enabled = optional(bool) 90 | servers = optional(list(string)) 91 | }) 92 | default = null 93 | description = <<-EOT 94 | - `proxy_enabled` - (Optional) Whether to enable DNS proxy on Firewalls attached to this Firewall Policy? Defaults to `false`. 95 | - `servers` - (Optional) A list of custom DNS servers' IP addresses. 96 | EOT 97 | } 98 | 99 | variable "firewall_policy_explicit_proxy" { 100 | type = object({ 101 | enable_pac_file = optional(bool) 102 | enabled = optional(bool) 103 | http_port = optional(number) 104 | https_port = optional(number) 105 | pac_file = optional(string) 106 | pac_file_port = optional(number) 107 | }) 108 | default = null 109 | description = <<-EOT 110 | - `enable_pac_file` - (Optional) Whether the pac file port and url need to be provided. 111 | - `enabled` - (Optional) Whether the explicit proxy is enabled for this Firewall Policy. 112 | - `http_port` - (Optional) The port number for explicit http protocol. 113 | - `https_port` - (Optional) The port number for explicit proxy https protocol. 114 | - `pac_file` - (Optional) Specifies a SAS URL for PAC file. 115 | - `pac_file_port` - (Optional) Specifies a port number for firewall to serve PAC file. 116 | EOT 117 | } 118 | 119 | variable "firewall_policy_identity" { 120 | type = object({ 121 | identity_ids = optional(set(string)) 122 | type = string 123 | }) 124 | default = null 125 | description = <<-EOT 126 | - `identity_ids` - (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this Firewall Policy. 127 | - `type` - (Required) Specifies the type of Managed Service Identity that should be configured on this Firewall Policy. Only possible value is `UserAssigned`. 128 | EOT 129 | } 130 | 131 | variable "firewall_policy_insights" { 132 | type = object({ 133 | default_log_analytics_workspace_id = string 134 | enabled = bool 135 | retention_in_days = optional(number) 136 | log_analytics_workspace = optional(list(object({ 137 | firewall_location = string 138 | id = string 139 | }))) 140 | }) 141 | default = null 142 | description = <<-EOT 143 | - `default_log_analytics_workspace_id` - (Required) The ID of the default Log Analytics Workspace that the Firewalls associated with this Firewall Policy will send their logs to, when there is no location matches in the `log_analytics_workspace`. 144 | - `enabled` - (Required) Whether the insights functionality is enabled for this Firewall Policy. 145 | - `retention_in_days` - (Optional) The log retention period in days. 146 | 147 | --- 148 | `log_analytics_workspace` block supports the following: 149 | - `firewall_location` - (Required) The location of the Firewalls, that when matches this Log Analytics Workspace will be used to consume their logs. 150 | - `id` - (Required) The ID of the Log Analytics Workspace that the Firewalls associated with this Firewall Policy will send their logs to when their locations match the `firewall_location`. 151 | EOT 152 | } 153 | 154 | variable "firewall_policy_intrusion_detection" { 155 | type = object({ 156 | mode = optional(string) 157 | private_ranges = optional(list(string)) 158 | signature_overrides = optional(list(object({ 159 | id = optional(string) 160 | state = optional(string) 161 | }))) 162 | traffic_bypass = optional(list(object({ 163 | description = optional(string) 164 | destination_addresses = optional(set(string)) 165 | destination_ip_groups = optional(set(string)) 166 | destination_ports = optional(set(string)) 167 | name = string 168 | protocol = string 169 | source_addresses = optional(set(string)) 170 | source_ip_groups = optional(set(string)) 171 | }))) 172 | }) 173 | default = null 174 | description = <<-EOT 175 | - `mode` - (Optional) In which mode you want to run intrusion detection: `Off`, `Alert` or `Deny`. 176 | - `private_ranges` - (Optional) A list of Private IP address ranges to identify traffic direction. By default, only ranges defined by IANA RFC 1918 are considered private IP addresses. 177 | 178 | --- 179 | `signature_overrides` block supports the following: 180 | - `id` - (Optional) 12-digit number (id) which identifies your signature. 181 | - `state` - (Optional) state can be any of `Off`, `Alert` or `Deny`. 182 | 183 | --- 184 | `traffic_bypass` block supports the following: 185 | - `description` - (Optional) The description for this bypass traffic setting. 186 | - `destination_addresses` - (Optional) Specifies a list of destination IP addresses that shall be bypassed by intrusion detection. 187 | - `destination_ip_groups` - (Optional) Specifies a list of destination IP groups that shall be bypassed by intrusion detection. 188 | - `destination_ports` - (Optional) Specifies a list of destination IP ports that shall be bypassed by intrusion detection. 189 | - `name` - (Required) The name which should be used for this bypass traffic setting. 190 | - `protocol` - (Required) The protocols any of `ANY`, `TCP`, `ICMP`, `UDP` that shall be bypassed by intrusion detection. 191 | - `source_addresses` - (Optional) Specifies a list of source addresses that shall be bypassed by intrusion detection. 192 | - `source_ip_groups` - (Optional) Specifies a list of source IP groups that shall be bypassed by intrusion detection. 193 | EOT 194 | } 195 | 196 | variable "firewall_policy_private_ip_ranges" { 197 | type = list(string) 198 | default = null 199 | description = "(Optional) A list of private IP ranges to which traffic will not be SNAT." 200 | } 201 | 202 | variable "firewall_policy_sku" { 203 | type = string 204 | default = null 205 | description = "(Optional) The SKU Tier of the Firewall Policy. Possible values are `Standard`, `Premium` and `Basic`. Changing this forces a new Firewall Policy to be created." 206 | } 207 | 208 | variable "firewall_policy_sql_redirect_allowed" { 209 | type = bool 210 | default = null 211 | description = "(Optional) Whether SQL Redirect traffic filtering is allowed. Enabling this flag requires no rule using ports between `11000`-`11999`." 212 | } 213 | 214 | variable "firewall_policy_threat_intelligence_allowlist" { 215 | type = object({ 216 | fqdns = optional(set(string)) 217 | ip_addresses = optional(set(string)) 218 | }) 219 | default = null 220 | description = <<-EOT 221 | - `fqdns` - (Optional) A list of FQDNs that will be skipped for threat detection. 222 | - `ip_addresses` - (Optional) A list of IP addresses or CIDR ranges that will be skipped for threat detection. 223 | EOT 224 | } 225 | 226 | variable "firewall_policy_threat_intelligence_mode" { 227 | type = string 228 | default = null 229 | description = "(Optional) The operation mode for Threat Intelligence. Possible values are `Alert`, `Deny` and `Off`. Defaults to `Alert`." 230 | } 231 | 232 | variable "firewall_policy_timeouts" { 233 | type = object({ 234 | create = optional(string) 235 | delete = optional(string) 236 | read = optional(string) 237 | update = optional(string) 238 | }) 239 | default = null 240 | description = <<-EOT 241 | - `create` - (Defaults to 30 minutes) Used when creating the Firewall Policy. 242 | - `delete` - (Defaults to 30 minutes) Used when deleting the Firewall Policy. 243 | - `read` - (Defaults to 5 minutes) Used when retrieving the Firewall Policy. 244 | - `update` - (Defaults to 30 minutes) Used when updating the Firewall Policy. 245 | EOT 246 | } 247 | 248 | variable "firewall_policy_tls_certificate" { 249 | type = object({ 250 | key_vault_secret_id = string 251 | name = string 252 | }) 253 | default = null 254 | description = <<-EOT 255 | - `key_vault_secret_id` - (Required) The ID of the Key Vault, where the secret or certificate is stored. 256 | - `name` - (Required) The name of the certificate. 257 | EOT 258 | } 259 | 260 | variable "lock" { 261 | type = object({ 262 | kind = string 263 | name = optional(string, null) 264 | }) 265 | default = null 266 | description = <. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time. 293 | 294 | - `role_definition_id_or_name` - The ID or name of the role definition to assign to the principal. 295 | - `principal_id` - The ID of the principal to assign the role to. 296 | - `description` - (Optional) The description of the role assignment. 297 | - `skip_service_principal_aad_check` - (Optional) If set to true, skips the Azure Active Directory check for the service principal in the tenant. Defaults to false. 298 | - `condition` - (Optional) The condition which will be used to scope the role assignment. 299 | - `condition_version` - (Optional) The version of the condition syntax. Leave as `null` if you are not using a condition, if you are then valid values are '2.0'. 300 | - `delegated_managed_identity_resource_id` - (Optional) The delegated Azure Resource Id which contains a Managed Identity. Changing this forces a new resource to be created. This field is only used in cross-tenant scenario. 301 | - `principal_type` - (Optional) The type of the `principal_id`. Possible values are `User`, `Group` and `ServicePrincipal`. It is necessary to explicitly set this attribute when creating role assignments if the principal creating the assignment is constrained by ABAC rules that filters on the PrincipalType attribute. 302 | 303 | > Note: only set `skip_service_principal_aad_check` to true if you are assigning a role to a service principal. 304 | DESCRIPTION 305 | nullable = false 306 | } 307 | 308 | variable "tags" { 309 | type = map(string) 310 | default = null 311 | description = "(Optional) A mapping of tags to assign to the resource." 312 | } 313 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # terraform-azurerm-avm-network-firewallpolicy 4 | 5 | This is the module to create an Azure Firewall Policy 6 | 7 | "Major version Zero (0.y.z) is for initial development. Anything MAY change at any time. The module SHOULD NOT be considered stable till at least it is major version one (1.0.0) or greater. Changes will always be via new versions being published and no changes will be made to existing published versions. For more details please go to " 8 | 9 | 10 | ## Requirements 11 | 12 | The following requirements are needed by this module: 13 | 14 | - [terraform](#requirement\_terraform) (~> 1.5) 15 | 16 | - [azapi](#requirement\_azapi) (~> 2.4) 17 | 18 | - [azurerm](#requirement\_azurerm) (>= 3.71, < 5.0.0) 19 | 20 | - [modtm](#requirement\_modtm) (~> 0.3) 21 | 22 | - [random](#requirement\_random) (~> 3.5) 23 | 24 | ## Resources 25 | 26 | The following resources are used by this module: 27 | 28 | - [azurerm_firewall_policy.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/firewall_policy) (resource) 29 | - [azurerm_management_lock.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/management_lock) (resource) 30 | - [azurerm_monitor_diagnostic_setting.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_diagnostic_setting) (resource) 31 | - [azurerm_role_assignment.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) (resource) 32 | - [modtm_telemetry.telemetry](https://registry.terraform.io/providers/Azure/modtm/latest/docs/resources/telemetry) (resource) 33 | - [random_uuid.telemetry](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/uuid) (resource) 34 | - [azapi_client_config.telemetry](https://registry.terraform.io/providers/Azure/azapi/latest/docs/data-sources/client_config) (data source) 35 | - [modtm_module_source.telemetry](https://registry.terraform.io/providers/Azure/modtm/latest/docs/data-sources/module_source) (data source) 36 | 37 | 38 | ## Required Inputs 39 | 40 | The following input variables are required: 41 | 42 | ### [location](#input\_location) 43 | 44 | Description: (Required) The Azure Region where the Firewall Policy should exist. Changing this forces a new Firewall Policy to be created. 45 | 46 | Type: `string` 47 | 48 | ### [name](#input\_name) 49 | 50 | Description: (Required) The name which should be used for this Firewall Policy. Changing this forces a new Firewall Policy to be created. 51 | 52 | Type: `string` 53 | 54 | ### [resource\_group\_name](#input\_resource\_group\_name) 55 | 56 | Description: (Required) The name of the Resource Group where the Firewall Policy should exist. Changing this forces a new Firewall Policy to be created. 57 | 58 | Type: `string` 59 | 60 | ## Optional Inputs 61 | 62 | The following input variables are optional (have default values): 63 | 64 | ### [diagnostic\_settings](#input\_diagnostic\_settings) 65 | 66 | Description: A map of diagnostic settings to create on the Key Vault. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time. 67 | 68 | - `name` - (Optional) The name of the diagnostic setting. One will be generated if not set, however this will not be unique if you want to create multiple diagnostic setting resources. 69 | - `log_categories` - (Optional) A set of log categories to send to the log analytics workspace. Defaults to `[]`. 70 | - `log_groups` - (Optional) A set of log groups to send to the log analytics workspace. Defaults to `["allLogs"]`. 71 | - `metric_categories` - (Optional) A set of metric categories to send to the log analytics workspace. Defaults to `["AllMetrics"]`. 72 | - `log_analytics_destination_type` - (Optional) The destination type for the diagnostic setting. Possible values are `Dedicated` and `AzureDiagnostics`. Defaults to `Dedicated`. 73 | - `workspace_resource_id` - (Optional) The resource ID of the log analytics workspace to send logs and metrics to. 74 | - `storage_account_resource_id` - (Optional) The resource ID of the storage account to send logs and metrics to. 75 | - `event_hub_authorization_rule_resource_id` - (Optional) The resource ID of the event hub authorization rule to send logs and metrics to. 76 | - `event_hub_name` - (Optional) The name of the event hub. If none is specified, the default event hub will be selected. 77 | - `marketplace_partner_resource_id` - (Optional) The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic LogsLogs. 78 | 79 | Type: 80 | 81 | ```hcl 82 | map(object({ 83 | name = optional(string, null) 84 | log_categories = optional(set(string), []) 85 | log_groups = optional(set(string), ["allLogs"]) 86 | metric_categories = optional(set(string), ["AllMetrics"]) 87 | log_analytics_destination_type = optional(string, "Dedicated") 88 | workspace_resource_id = optional(string, null) 89 | storage_account_resource_id = optional(string, null) 90 | event_hub_authorization_rule_resource_id = optional(string, null) 91 | event_hub_name = optional(string, null) 92 | marketplace_partner_resource_id = optional(string, null) 93 | })) 94 | ``` 95 | 96 | Default: `{}` 97 | 98 | ### [enable\_telemetry](#input\_enable\_telemetry) 99 | 100 | Description: This variable controls whether or not telemetry is enabled for the module. 101 | For more information see . 102 | If it is set to false, then no telemetry will be collected. 103 | 104 | Type: `bool` 105 | 106 | Default: `true` 107 | 108 | ### [firewall\_policy\_auto\_learn\_private\_ranges\_enabled](#input\_firewall\_policy\_auto\_learn\_private\_ranges\_enabled) 109 | 110 | Description: (Optional) Whether enable auto learn private ip range. 111 | 112 | Type: `bool` 113 | 114 | Default: `null` 115 | 116 | ### [firewall\_policy\_base\_policy\_id](#input\_firewall\_policy\_base\_policy\_id) 117 | 118 | Description: (Optional) The ID of the base Firewall Policy. 119 | 120 | Type: `string` 121 | 122 | Default: `null` 123 | 124 | ### [firewall\_policy\_dns](#input\_firewall\_policy\_dns) 125 | 126 | Description: - `proxy_enabled` - (Optional) Whether to enable DNS proxy on Firewalls attached to this Firewall Policy? Defaults to `false`. 127 | - `servers` - (Optional) A list of custom DNS servers' IP addresses. 128 | 129 | Type: 130 | 131 | ```hcl 132 | object({ 133 | proxy_enabled = optional(bool) 134 | servers = optional(list(string)) 135 | }) 136 | ``` 137 | 138 | Default: `null` 139 | 140 | ### [firewall\_policy\_explicit\_proxy](#input\_firewall\_policy\_explicit\_proxy) 141 | 142 | Description: - `enable_pac_file` - (Optional) Whether the pac file port and url need to be provided. 143 | - `enabled` - (Optional) Whether the explicit proxy is enabled for this Firewall Policy. 144 | - `http_port` - (Optional) The port number for explicit http protocol. 145 | - `https_port` - (Optional) The port number for explicit proxy https protocol. 146 | - `pac_file` - (Optional) Specifies a SAS URL for PAC file. 147 | - `pac_file_port` - (Optional) Specifies a port number for firewall to serve PAC file. 148 | 149 | Type: 150 | 151 | ```hcl 152 | object({ 153 | enable_pac_file = optional(bool) 154 | enabled = optional(bool) 155 | http_port = optional(number) 156 | https_port = optional(number) 157 | pac_file = optional(string) 158 | pac_file_port = optional(number) 159 | }) 160 | ``` 161 | 162 | Default: `null` 163 | 164 | ### [firewall\_policy\_identity](#input\_firewall\_policy\_identity) 165 | 166 | Description: - `identity_ids` - (Optional) Specifies a list of User Assigned Managed Identity IDs to be assigned to this Firewall Policy. 167 | - `type` - (Required) Specifies the type of Managed Service Identity that should be configured on this Firewall Policy. Only possible value is `UserAssigned`. 168 | 169 | Type: 170 | 171 | ```hcl 172 | object({ 173 | identity_ids = optional(set(string)) 174 | type = string 175 | }) 176 | ``` 177 | 178 | Default: `null` 179 | 180 | ### [firewall\_policy\_insights](#input\_firewall\_policy\_insights) 181 | 182 | Description: - `default_log_analytics_workspace_id` - (Required) The ID of the default Log Analytics Workspace that the Firewalls associated with this Firewall Policy will send their logs to, when there is no location matches in the `log_analytics_workspace`. 183 | - `enabled` - (Required) Whether the insights functionality is enabled for this Firewall Policy. 184 | - `retention_in_days` - (Optional) The log retention period in days. 185 | 186 | --- 187 | `log_analytics_workspace` block supports the following: 188 | - `firewall_location` - (Required) The location of the Firewalls, that when matches this Log Analytics Workspace will be used to consume their logs. 189 | - `id` - (Required) The ID of the Log Analytics Workspace that the Firewalls associated with this Firewall Policy will send their logs to when their locations match the `firewall_location`. 190 | 191 | Type: 192 | 193 | ```hcl 194 | object({ 195 | default_log_analytics_workspace_id = string 196 | enabled = bool 197 | retention_in_days = optional(number) 198 | log_analytics_workspace = optional(list(object({ 199 | firewall_location = string 200 | id = string 201 | }))) 202 | }) 203 | ``` 204 | 205 | Default: `null` 206 | 207 | ### [firewall\_policy\_intrusion\_detection](#input\_firewall\_policy\_intrusion\_detection) 208 | 209 | Description: - `mode` - (Optional) In which mode you want to run intrusion detection: `Off`, `Alert` or `Deny`. 210 | - `private_ranges` - (Optional) A list of Private IP address ranges to identify traffic direction. By default, only ranges defined by IANA RFC 1918 are considered private IP addresses. 211 | 212 | --- 213 | `signature_overrides` block supports the following: 214 | - `id` - (Optional) 12-digit number (id) which identifies your signature. 215 | - `state` - (Optional) state can be any of `Off`, `Alert` or `Deny`. 216 | 217 | --- 218 | `traffic_bypass` block supports the following: 219 | - `description` - (Optional) The description for this bypass traffic setting. 220 | - `destination_addresses` - (Optional) Specifies a list of destination IP addresses that shall be bypassed by intrusion detection. 221 | - `destination_ip_groups` - (Optional) Specifies a list of destination IP groups that shall be bypassed by intrusion detection. 222 | - `destination_ports` - (Optional) Specifies a list of destination IP ports that shall be bypassed by intrusion detection. 223 | - `name` - (Required) The name which should be used for this bypass traffic setting. 224 | - `protocol` - (Required) The protocols any of `ANY`, `TCP`, `ICMP`, `UDP` that shall be bypassed by intrusion detection. 225 | - `source_addresses` - (Optional) Specifies a list of source addresses that shall be bypassed by intrusion detection. 226 | - `source_ip_groups` - (Optional) Specifies a list of source IP groups that shall be bypassed by intrusion detection. 227 | 228 | Type: 229 | 230 | ```hcl 231 | object({ 232 | mode = optional(string) 233 | private_ranges = optional(list(string)) 234 | signature_overrides = optional(list(object({ 235 | id = optional(string) 236 | state = optional(string) 237 | }))) 238 | traffic_bypass = optional(list(object({ 239 | description = optional(string) 240 | destination_addresses = optional(set(string)) 241 | destination_ip_groups = optional(set(string)) 242 | destination_ports = optional(set(string)) 243 | name = string 244 | protocol = string 245 | source_addresses = optional(set(string)) 246 | source_ip_groups = optional(set(string)) 247 | }))) 248 | }) 249 | ``` 250 | 251 | Default: `null` 252 | 253 | ### [firewall\_policy\_private\_ip\_ranges](#input\_firewall\_policy\_private\_ip\_ranges) 254 | 255 | Description: (Optional) A list of private IP ranges to which traffic will not be SNAT. 256 | 257 | Type: `list(string)` 258 | 259 | Default: `null` 260 | 261 | ### [firewall\_policy\_sku](#input\_firewall\_policy\_sku) 262 | 263 | Description: (Optional) The SKU Tier of the Firewall Policy. Possible values are `Standard`, `Premium` and `Basic`. Changing this forces a new Firewall Policy to be created. 264 | 265 | Type: `string` 266 | 267 | Default: `null` 268 | 269 | ### [firewall\_policy\_sql\_redirect\_allowed](#input\_firewall\_policy\_sql\_redirect\_allowed) 270 | 271 | Description: (Optional) Whether SQL Redirect traffic filtering is allowed. Enabling this flag requires no rule using ports between `11000`-`11999`. 272 | 273 | Type: `bool` 274 | 275 | Default: `null` 276 | 277 | ### [firewall\_policy\_threat\_intelligence\_allowlist](#input\_firewall\_policy\_threat\_intelligence\_allowlist) 278 | 279 | Description: - `fqdns` - (Optional) A list of FQDNs that will be skipped for threat detection. 280 | - `ip_addresses` - (Optional) A list of IP addresses or CIDR ranges that will be skipped for threat detection. 281 | 282 | Type: 283 | 284 | ```hcl 285 | object({ 286 | fqdns = optional(set(string)) 287 | ip_addresses = optional(set(string)) 288 | }) 289 | ``` 290 | 291 | Default: `null` 292 | 293 | ### [firewall\_policy\_threat\_intelligence\_mode](#input\_firewall\_policy\_threat\_intelligence\_mode) 294 | 295 | Description: (Optional) The operation mode for Threat Intelligence. Possible values are `Alert`, `Deny` and `Off`. Defaults to `Alert`. 296 | 297 | Type: `string` 298 | 299 | Default: `null` 300 | 301 | ### [firewall\_policy\_timeouts](#input\_firewall\_policy\_timeouts) 302 | 303 | Description: - `create` - (Defaults to 30 minutes) Used when creating the Firewall Policy. 304 | - `delete` - (Defaults to 30 minutes) Used when deleting the Firewall Policy. 305 | - `read` - (Defaults to 5 minutes) Used when retrieving the Firewall Policy. 306 | - `update` - (Defaults to 30 minutes) Used when updating the Firewall Policy. 307 | 308 | Type: 309 | 310 | ```hcl 311 | object({ 312 | create = optional(string) 313 | delete = optional(string) 314 | read = optional(string) 315 | update = optional(string) 316 | }) 317 | ``` 318 | 319 | Default: `null` 320 | 321 | ### [firewall\_policy\_tls\_certificate](#input\_firewall\_policy\_tls\_certificate) 322 | 323 | Description: - `key_vault_secret_id` - (Required) The ID of the Key Vault, where the secret or certificate is stored. 324 | - `name` - (Required) The name of the certificate. 325 | 326 | Type: 327 | 328 | ```hcl 329 | object({ 330 | key_vault_secret_id = string 331 | name = string 332 | }) 333 | ``` 334 | 335 | Default: `null` 336 | 337 | ### [lock](#input\_lock) 338 | 339 | Description: Controls the Resource Lock configuration for this resource. The following properties can be specified: 340 | 341 | - `kind` - (Required) The type of lock. Possible values are `\"CanNotDelete\"` and `\"ReadOnly\"`. 342 | - `name` - (Optional) The name of the lock. If not specified, a name will be generated based on the `kind` value. Changing this forces the creation of a new resource. 343 | 344 | Type: 345 | 346 | ```hcl 347 | object({ 348 | kind = string 349 | name = optional(string, null) 350 | }) 351 | ``` 352 | 353 | Default: `null` 354 | 355 | ### [role\_assignments](#input\_role\_assignments) 356 | 357 | Description: A map of role assignments to create on the . The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time. 358 | 359 | - `role_definition_id_or_name` - The ID or name of the role definition to assign to the principal. 360 | - `principal_id` - The ID of the principal to assign the role to. 361 | - `description` - (Optional) The description of the role assignment. 362 | - `skip_service_principal_aad_check` - (Optional) If set to true, skips the Azure Active Directory check for the service principal in the tenant. Defaults to false. 363 | - `condition` - (Optional) The condition which will be used to scope the role assignment. 364 | - `condition_version` - (Optional) The version of the condition syntax. Leave as `null` if you are not using a condition, if you are then valid values are '2.0'. 365 | - `delegated_managed_identity_resource_id` - (Optional) The delegated Azure Resource Id which contains a Managed Identity. Changing this forces a new resource to be created. This field is only used in cross-tenant scenario. 366 | - `principal_type` - (Optional) The type of the `principal_id`. Possible values are `User`, `Group` and `ServicePrincipal`. It is necessary to explicitly set this attribute when creating role assignments if the principal creating the assignment is constrained by ABAC rules that filters on the PrincipalType attribute. 367 | 368 | > Note: only set `skip_service_principal_aad_check` to true if you are assigning a role to a service principal. 369 | 370 | Type: 371 | 372 | ```hcl 373 | map(object({ 374 | role_definition_id_or_name = string 375 | principal_id = string 376 | description = optional(string, null) 377 | skip_service_principal_aad_check = optional(bool, false) 378 | condition = optional(string, null) 379 | condition_version = optional(string, null) 380 | delegated_managed_identity_resource_id = optional(string, null) 381 | principal_type = optional(string, null) 382 | })) 383 | ``` 384 | 385 | Default: `{}` 386 | 387 | ### [tags](#input\_tags) 388 | 389 | Description: (Optional) A mapping of tags to assign to the resource. 390 | 391 | Type: `map(string)` 392 | 393 | Default: `null` 394 | 395 | ## Outputs 396 | 397 | The following outputs are exported: 398 | 399 | ### [resource](#output\_resource) 400 | 401 | Description: "This is the full output for Firewall Policy resource. This is the default output for the module following AVM standards. Review the examples below for the correct output to use in your module." 402 | Examples: 403 | - module.firewall\_policy.resource.id 404 | - module.firewall\_policy.resource.firewalls 405 | - module.firewall\_policy.resource.child\_policies 406 | - module.firewall\_policy.resource.rule\_collection\_groups 407 | 408 | ### [resource\_id](#output\_resource\_id) 409 | 410 | Description: the resource id of the firewall policy 411 | 412 | ## Modules 413 | 414 | No modules. 415 | 416 | 417 | ## Data Collection 418 | 419 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. 420 | --------------------------------------------------------------------------------