├── .checkov.yml ├── .codespellrc ├── .cspell.yml ├── .devcontainer ├── README.md ├── devcontainer.env └── devcontainer.json ├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── example_request.yml │ └── question.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── check_pr.yml │ ├── dependency_review.yml │ └── semantic_pr.yml ├── .gitignore ├── .gitleaks.toml ├── .golangci.yml ├── .lychee.toml ├── .markdownlint-cli2.yaml ├── .markdownlint.yml ├── .shellcheckrc ├── .taskfiles ├── github.yml ├── golang.yml ├── internal.yml ├── markdown.yml ├── security.yml ├── spell.yml ├── terraform.yml └── yaml.yml ├── .terraform-docs.yml ├── .tflint.hcl ├── .trivy.yml ├── .trivyignore.yml ├── .vscode ├── extensions.json ├── mcp.json └── settings.json ├── .yamllint.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEVELOPER.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── Taskfile.yml ├── quickstarts ├── 101-hello-fabric │ ├── README.md │ ├── _footer.md │ ├── _header.md │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── tests │ │ ├── test_acc.tftest.hcl │ │ └── test_unit.tftest.hcl │ └── variables.tf ├── 102-fabric-capacity │ ├── README.md │ ├── _footer.md │ ├── _header.md │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── tests │ │ └── test_unit.tftest.hcl │ └── variables.tf ├── 202-fabric-vnet-gateway │ ├── README.md │ ├── _footer.md │ ├── _header.md │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── tests │ │ └── test_unit.tftest.hcl │ └── variables.tf ├── 301-testing-terraform │ ├── README.md │ ├── _footer.md │ ├── _header.md │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── tests │ │ ├── test_input.tftest.hcl │ │ └── test_unit.tftest.hcl │ └── variables.tf └── 401-testing-terratest │ ├── README.md │ ├── _header.md │ ├── go.mod │ ├── go.sum │ └── workspace_test.go └── tests ├── mocks ├── azuread │ └── data.tfmock.hcl ├── azurerm │ ├── data.tfmock.hcl │ └── resource.tfmock.hcl └── fabric │ ├── data.tfmock.hcl │ └── resource.tfmock.hcl └── random_generator ├── README.md ├── _footer.md ├── _header.md ├── main.tf ├── outputs.tf ├── tests └── test_unit.tftest.hcl └── variables.tf /.checkov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | quiet: true 3 | download-external-modules: true 4 | skip-framework: 5 | - terraform 6 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = ./megalinter-reports,*.svg,*.sum 3 | ignore-words-list = CAF 4 | count = true 5 | -------------------------------------------------------------------------------- /.cspell.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json 2 | --- 3 | $schema: https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json 4 | version: "0.2" 5 | language: en 6 | dictionaries: 7 | - en_US 8 | - companies 9 | - softwareTerms 10 | - misc 11 | - go 12 | - python 13 | - powershell 14 | - bash 15 | - filetypes 16 | words: 17 | - Entra 18 | - vnet 19 | - tftest 20 | - MSRC 21 | - upns 22 | - azuread 23 | - tfenv 24 | - goenv 25 | - tfutils 26 | - golangci 27 | - govulncheck 28 | - stefanzweifel 29 | - ghsa 30 | allowCompoundWords: true 31 | -------------------------------------------------------------------------------- /.devcontainer/README.md: -------------------------------------------------------------------------------- 1 | # DevContainer 2 | 3 | This DevContainer is intended to simplify local or [Codespaces](https://github.com/features/codespaces) development and ensure we're all using the same dependencies to build, test and debug. 4 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.env: -------------------------------------------------------------------------------- 1 | ZSH_DISABLE_COMPFIX=true 2 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json 2 | { 3 | "name": "Fabric Terraform Quickstart", 4 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 5 | // Available base images: https://mcr.microsoft.com/v2/devcontainers/base/tags/list 6 | "image": "mcr.microsoft.com/devcontainers/base:ubuntu24.04", 7 | // Features to add to the dev container. More info: https://containers.dev/features 8 | "features": { 9 | "ghcr.io/meaningful-ooo/devcontainer-features/fish:2": {}, 10 | "ghcr.io/devcontainers/features/common-utils:2": { 11 | "configureZshAsDefaultShell": true 12 | }, 13 | "ghcr.io/devcontainers/features/go:1": { 14 | "version": "1.24" 15 | }, 16 | "ghcr.io/devcontainers/features/python:1": { 17 | "version": "3.13", 18 | "toolsToInstall": "" 19 | }, 20 | "ghcr.io/devcontainers/features/node:1": {}, 21 | "ghcr.io/devcontainers/features/azure-cli:1": {}, 22 | "ghcr.io/devcontainers/features/github-cli:1": {}, 23 | "ghcr.io/devcontainers/features/powershell:1": {}, 24 | "ghcr.io/eitsupi/devcontainer-features/jq-likes:2": { 25 | "jqVersion": "latest", 26 | "yqVersion": "latest", 27 | "gojqVersion": "latest", 28 | "jaqVersion": "latest" 29 | }, 30 | "ghcr.io/devcontainers/features/terraform:1": { 31 | "version": "1.12.1", 32 | "installSentinel": true, 33 | "installTFsec": false, 34 | "installTerraformDocs": true 35 | }, 36 | "ghcr.io/robbert229/devcontainer-features/opentofu:1": { 37 | "version": "1.9.1" 38 | }, 39 | "ghcr.io/eitsupi/devcontainer-features/go-task:1": {} 40 | }, 41 | "onCreateCommand": { 42 | "chown-local": "sudo chown -R $(id -un):$(id -gn) $HOME/.local", 43 | "setup-tools": "task tools" 44 | }, 45 | "postCreateCommand": { 46 | "git-safe-dir": "git config --global --add safe.directory ${containerWorkspaceFolder}" 47 | }, 48 | "remoteUser": "vscode", 49 | "remoteEnv": { 50 | "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" 51 | }, 52 | "workspaceFolder": "/workspace", 53 | "workspaceMount": "source=${localWorkspaceFolder},target=${containerWorkspaceFolder},type=bind,consistency=cached", 54 | "customizations": { 55 | "vscode": { 56 | "settings": { 57 | "terminal.integrated.defaultProfile.linux": "zsh", 58 | "terminal.integrated.defaultProfile.osx": "zsh", 59 | "powershell.powerShellAdditionalExePaths": { 60 | "pwsh": "/usr/bin/pwsh" 61 | }, 62 | "powershell.powerShellDefaultVersion": "pwsh" 63 | }, 64 | "extensions": [ 65 | "golang.go", 66 | "foxundermoon.shell-format", 67 | "redhat.vscode-yaml", 68 | "ms-vscode.powershell", 69 | "ms-vscode.azurecli", 70 | "GitHub.vscode-pull-request-github", 71 | "GitHub.copilot-chat", 72 | "GitHub.codespaces", 73 | "GitHub.remotehub", 74 | "GitHub.copilot", 75 | "GitHub.vscode-github-actions", 76 | "EditorConfig.EditorConfig", 77 | "eamodio.gitlens", 78 | "DavidAnson.vscode-markdownlint", 79 | "bierner.github-markdown-preview", 80 | "TakumiI.markdowntable", 81 | "hashicorp.terraform", 82 | "hashicorp.hcl", 83 | "ms-azuretools.vscode-azureterraform", 84 | "ms-azuretools.vscode-docker", 85 | "ms-vscode-remote.vscode-remote-extensionpack", 86 | "ms-vscode.remote-explorer", 87 | "ms-vscode.remote-repositories", 88 | "ms-vscode-remote.remote-ssh-edit", 89 | "fnando.linter", 90 | "task.vscode-task", 91 | "aaron-bond.better-comments", 92 | "usernamehw.errorlens", 93 | "MS-SarifVSCode.sarif-viewer", 94 | "SanjulaGanepola.github-local-actions" 95 | ] 96 | } 97 | }, 98 | "runArgs": [ 99 | "--env-file", 100 | ".devcontainer/devcontainer.env" 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | 11 | [*.{cmd,bat}] 12 | end_of_line = crlf 13 | 14 | [*.md] 15 | indent_size = unset 16 | 17 | [*.py] 18 | indent_size = 4 19 | 20 | [*.go] 21 | indent_style = tab 22 | 23 | [*.ps1] 24 | indent_size = 4 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Force the following filetypes to have unix eols, so Windows does not break them 2 | * text eol=lf 3 | 4 | # Declare files that will always have CRLF line endings on checkout. 5 | *.{cmd,[cC][mM][dD]} text eol=crlf 6 | *.{bat,[bB][aA][tT]} text eol=crlf 7 | 8 | # Common files config 9 | *.pdf binary 10 | *.gif binary 11 | *.tif binary 12 | *.ico binary 13 | *.jpg binary 14 | *.jpeg binary 15 | *.png binary 16 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default codeowners 2 | * @microsoft/fabrictf-review 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-issue-forms.json 2 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 3 | --- 4 | name: 🐛 Bug Report 5 | description: If something isn't working 🔧 6 | title: "[bug] " 7 | labels: ["bug"] 8 | body: 9 | - type: markdown 10 | attributes: 11 | value: Thanks for taking the time to fill out this bug report! 12 | 13 | - type: textarea 14 | id: what-happened 15 | attributes: 16 | label: 🐛 What happened? 17 | description: A clear and concise description of what the bug is. 18 | placeholder: Describe what happened 19 | validations: 20 | required: true 21 | 22 | - type: textarea 23 | id: reproduce 24 | attributes: 25 | label: 🔬 How to reproduce? 26 | description: Steps to reproduce the behavior. 27 | placeholder: | 28 | 1. Go to... 29 | 2. Click on... 30 | 3. Run ... 31 | 4. Scroll down to... 32 | 5. See error 33 | validations: 34 | required: false 35 | 36 | - type: textarea 37 | id: code-sample 38 | attributes: 39 | label: 🏗️ Code Sample / Log 40 | description: | 41 | Please copy and paste any relevant code sample / log output to help explain your problem. 42 | 43 | ‼️ **REMINDER: REMOVE SENSITIVE DATA SUCH AS SECRETS, USER NAMES, EMAILS, TENANT INFORMATION, ETC.** 44 | placeholder: code sample / log output to help explain your problem. 45 | validations: 46 | required: false 47 | 48 | - type: textarea 49 | id: screenshots 50 | attributes: 51 | label: 📷 Screenshots 52 | description: If applicable, add screenshots to help explain your problem. 53 | placeholder: Just do Ctrl+V having screenshot in the clipboard. 54 | validations: 55 | required: false 56 | 57 | - type: textarea 58 | id: expected-behavior 59 | attributes: 60 | label: 📈 Expected behavior 61 | description: A clear and concise description of what you expected to happen. 62 | placeholder: A clear and concise description of what you expected to happen. 63 | validations: 64 | required: false 65 | 66 | - type: input 67 | id: provider-version 68 | attributes: 69 | label: 🌌 Environment (Provider Version) 70 | description: What Terraform Provider version are you running? 71 | placeholder: 0.0.1-preview 72 | validations: 73 | required: true 74 | 75 | - type: input 76 | id: terraform-version 77 | attributes: 78 | label: 🌌 Environment (Terraform Version) 79 | description: What Terraform version are you running? Run `terraform version` to see it. 80 | placeholder: 1.8.3 81 | validations: 82 | required: true 83 | 84 | - type: dropdown 85 | id: os 86 | attributes: 87 | label: 🌌 Environment (OS) 88 | description: What OS are you using to run the Provider? 89 | options: 90 | - Linux 91 | - Windows 92 | - macOS 93 | validations: 94 | required: true 95 | 96 | - type: textarea 97 | id: additional-context 98 | attributes: 99 | label: 📎 Additional context 100 | description: Add any other context about the problem here. 101 | placeholder: Add any other context about the problem here. 102 | validations: 103 | required: false 104 | 105 | - type: checkboxes 106 | id: terms 107 | attributes: 108 | label: 🔰 Code of Conduct 109 | description: By submitting this issue, you agree to follow our [`Code of Conduct`](../blob/main/CODE_OF_CONDUCT.md) 110 | options: 111 | - label: I agree to follow this project's Code of Conduct. 112 | required: true 113 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-issue-config.json 2 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 3 | --- 4 | blank_issues_enabled: false 5 | contact_links: 6 | - name: Security Bug Report 7 | url: https://aka.ms/opensource/security/create-report 8 | about: Please report security vulnerabilities here. DO NOT post via Issues. 9 | - name: Microsoft Fabric Community 10 | url: https://aka.ms/FabricCommunity 11 | about: Connect with community members, ask questions, and learn more about Fabric. 12 | - name: Microsoft Fabric Ideas 13 | url: https://aka.ms/FabricIdeas 14 | about: Share ideas and feature requests for Fabric. 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/example_request.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-issue-forms.json 2 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 3 | --- 4 | name: 💫 Quickstart Example Request 5 | description: Request a new quickstart example to be added to the Terraform Provider repository 📢 6 | title: "[guide] " 7 | labels: [guide] 8 | body: 9 | - type: textarea 10 | id: description 11 | attributes: 12 | label: 📝 Description 13 | description: | 14 | Short description here describing the new quick-start example that you're requesting. 15 | Include a use case for why users need this example. 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | id: details 21 | attributes: 22 | label: 🔬 Details / References 23 | description: Example Details 24 | placeholder: | 25 | - Proposed Name: (e.g. 101-my-example) 26 | - Supporting documentation: 27 | - Estimated complexity/effort: 28 | - Related resources/data sources: 29 | value: | 30 | - Proposed Name: (e.g. 101-my-example) 31 | - Supporting documentation: 32 | - Estimated complexity/effort: 33 | - Related resources/data sources: 34 | validations: 35 | required: false 36 | 37 | - type: textarea 38 | id: additional-context 39 | attributes: 40 | label: 📎 Additional context 41 | description: Add any other context or screenshots about the feature request here. 42 | placeholder: Add any other context or screenshots about the feature request here. 43 | validations: 44 | required: false 45 | 46 | - type: checkboxes 47 | id: done-definition 48 | attributes: 49 | label: ✅ Definition of Done 50 | description: Necessary criteria for a task or feature to be considered complete. 51 | options: 52 | - label: Example in the ./quickstarts folder 53 | required: false 54 | - label: Example documentation in README.md 55 | required: false 56 | - label: Confirmation that you have manually tested this 57 | required: false 58 | 59 | - type: checkboxes 60 | id: terms 61 | attributes: 62 | label: 🔰 Code of Conduct 63 | description: By submitting this issue, you agree to follow our [`Code of Conduct`](../blob/main/CODE_OF_CONDUCT.md) 64 | options: 65 | - label: I agree to follow this project's Code of Conduct. 66 | required: true 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-issue-forms.json 2 | # https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema 3 | --- 4 | name: ❓ Question 5 | description: Ask a question about this project 🎓 6 | title: "[question] " 7 | labels: [question] 8 | body: 9 | - type: checkboxes 10 | id: checklist 11 | attributes: 12 | label: ☑️ Checklist 13 | description: Mark with an ✔️ all the checkboxes that apply. 14 | options: 15 | - label: I've searched the project's [`issues`](../issues?q=is%3Aissue) and did not find answer for my question. 16 | required: true 17 | 18 | - type: textarea 19 | id: question 20 | attributes: 21 | label: ❓ Question 22 | description: What is your question? 23 | placeholder: | 24 | How can I ...? 25 | Is it possible to ...? 26 | validations: 27 | required: true 28 | 29 | - type: textarea 30 | id: additional-context 31 | attributes: 32 | label: 📎 Additional context 33 | description: Add any other context or screenshots about the question here. 34 | placeholder: Add any other context or screenshots about the question here. 35 | validations: 36 | required: false 37 | 38 | - type: checkboxes 39 | id: terms 40 | attributes: 41 | label: 🔰 Code of Conduct 42 | description: By submitting this issue, you agree to follow [`Code of Conduct`](../blob/main/CODE_OF_CONDUCT.md) 43 | options: 44 | - label: I agree to follow this project's Code of Conduct. 45 | required: true 46 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json 2 | # See GitHub's documentation for more information on this file: 3 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference 4 | --- 5 | version: 2 6 | 7 | enable-beta-ecosystems: true 8 | 9 | updates: 10 | - package-ecosystem: github-actions 11 | directory: / 12 | schedule: 13 | interval: daily 14 | commit-message: 15 | prefix: ci 16 | include: scope 17 | labels: 18 | - "deps/github-actions" 19 | groups: 20 | all: 21 | patterns: ["*"] 22 | 23 | - package-ecosystem: devcontainers 24 | directory: / 25 | schedule: 26 | interval: daily 27 | commit-message: 28 | prefix: build 29 | include: scope 30 | labels: 31 | - "deps/devcontainer" 32 | groups: 33 | all: 34 | patterns: ["*"] 35 | 36 | - package-ecosystem: gomod 37 | directories: 38 | - "/**/*" 39 | schedule: 40 | interval: daily 41 | commit-message: 42 | prefix: build 43 | prefix-development: chore 44 | include: scope 45 | labels: 46 | - "deps/go" 47 | groups: 48 | all: 49 | patterns: ["*"] 50 | 51 | - package-ecosystem: terraform 52 | directories: 53 | - "/**/*" 54 | schedule: 55 | interval: daily 56 | commit-message: 57 | prefix: build 58 | include: scope 59 | labels: 60 | - "deps/terraform" 61 | groups: 62 | all: 63 | patterns: ["*"] 64 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # 📥 Pull Request 2 | 3 | ## ❓ What are you trying to address 4 | 5 | - Describe the current behavior that you are modifying and link to issue number. 6 | - If you don't have an issue, browse through existing issues to see if this is already tracked as an issue, to assign yourself to the issue and also verify that no one else is already working on the issue. 7 | 8 | ## ✨ Description of new changes 9 | 10 | - Write a detailed description of all changes and, if appropriate, why they are needed. 11 | 12 | ## ☑️ PR Checklist 13 | 14 | - [ ] Link to the issue you are addressing is included above 15 | - [ ] Ensure the PR description clearly describes the feature you're adding and any known limitations 16 | -------------------------------------------------------------------------------- /.github/workflows/check_pr.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | # Linter to enforce semantic pull request titles (see https://www.conventionalcommits.org/) 4 | --- 5 | name: 🔍 Check PR 6 | on: 7 | pull_request: 8 | branches: 9 | - main 10 | types: 11 | - opened 12 | - synchronize 13 | merge_group: 14 | 15 | permissions: 16 | contents: write 17 | pull-requests: write 18 | 19 | concurrency: 20 | group: ${{ format('{0}-{1}-{2}-{3}-{4}', github.workflow, github.event_name, github.ref, github.base_ref || null, github.head_ref || null) }} 21 | cancel-in-progress: true 22 | 23 | env: 24 | PR_URL: ${{ github.event.pull_request_target.html_url || github.event.pull_request.html_url }} 25 | PR_NUMBER: ${{ github.event.pull_request_target.number || github.event.pull_request.number }} 26 | 27 | jobs: 28 | check_pr: 29 | name: Check PR 30 | runs-on: ubuntu-24.04 31 | steps: 32 | - name: 🩺 Debug 33 | uses: raven-actions/debug@13e7c5b2e0436a1b85276087eba43ec7d46bd955 # v1.1.0 34 | 35 | - name: ⤵️ Checkout 36 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 37 | 38 | - name: 🚧 Setup Node 39 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 40 | with: 41 | node-version: lts/* 42 | 43 | - name: 🚧 Setup Python 44 | uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 45 | with: 46 | python-version: 3.x 47 | 48 | - name: 🚧 Setup Go 49 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 50 | with: 51 | go-version: stable 52 | check-latest: true 53 | cache: false 54 | 55 | - name: 🚧 Setup Task 56 | uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0 57 | with: 58 | repo-token: ${{ github.token }} 59 | 60 | - name: 🔨 Setup tools 61 | run: task tools 62 | 63 | - name: 📃 Generate docs 64 | run: task docs 65 | 66 | - name: 🤖 Bot details 67 | if: github.event.pull_request.user.login == 'dependabot[bot]' 68 | id: bot-details 69 | uses: raven-actions/bot-details@b2d5fd6eb98adc0cb67df864daa834849f3a8bc0 # v1.1.0 70 | with: 71 | bot-slug-name: dependabot 72 | 73 | - name: 📤 Dependabot auto-commit (docs) 74 | uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5.2.0 75 | if: github.event.pull_request.user.login == 'dependabot[bot]' 76 | with: 77 | commit_message: "docs: automatic updates" 78 | commit_user_name: ${{ steps.bot-details.outputs.name }} 79 | commit_user_email: ${{ steps.bot-details.outputs.email }} 80 | commit_options: "--no-verify --signoff" 81 | 82 | - name: 🔀 Check for differences 83 | if: github.event.pull_request.user.login != 'dependabot[bot]' 84 | run: | 85 | git diff --compact-summary --exit-code || \ 86 | (echo; echo "🛑 Unexpected difference. Run 'task docs' command and commit."; git diff --exit-code) 87 | 88 | - name: ✔️ Run linters 89 | run: task lint 90 | 91 | - name: 📤 Dependabot auto-commit (lint) 92 | uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5.2.0 93 | if: github.event.pull_request.user.login == 'dependabot[bot]' 94 | with: 95 | commit_message: "style: linters automatic fixes" 96 | commit_user_name: ${{ steps.bot-details.outputs.name }} 97 | commit_user_email: ${{ steps.bot-details.outputs.email }} 98 | commit_options: "--no-verify --signoff" 99 | 100 | - name: 🔀 Check for differences 101 | if: github.event.pull_request.user.login != 'dependabot[bot]' 102 | run: | 103 | git diff --compact-summary --exit-code || \ 104 | (echo; echo "🛑 Unexpected difference. Run 'task lint' command and commit."; git diff --exit-code) 105 | 106 | - name: 📝 Fetch Dependabot metadata 107 | if: github.event.pull_request.user.login == 'dependabot[bot]' 108 | id: dependabot-metadata 109 | uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0 110 | with: 111 | alert-lookup: true 112 | compat-lookup: true 113 | 114 | - name: 🏷️ Label (security) 115 | if: github.event.pull_request.user.login == 'dependabot[bot]' && (steps.dependabot-metadata.outputs.ghsa-id != '' || steps.dependabot-metadata.outputs.cvss != 0) 116 | run: gh pr edit --add-label "area/security" "${PR_URL}" 117 | env: 118 | GITHUB_TOKEN: ${{ github.token }} 119 | 120 | - name: 🤝 Enable auto-merge 121 | if: github.event.pull_request.user.login == 'dependabot[bot]' 122 | run: gh pr merge --auto --squash "${PR_URL}" 123 | env: 124 | GITHUB_TOKEN: ${{ github.token }} 125 | 126 | - name: 🔂 Check for changes 127 | uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 128 | id: filter 129 | with: 130 | filters: | 131 | tf: 132 | - '**.tf' 133 | - '**.tfvars' 134 | - '**.tfvars.json' 135 | - '**.tftest.hcl' 136 | 137 | - name: 🧪 Run Terraform unit tests 138 | if: steps.filter.outputs.tf == 'true' 139 | run: task tf:test:unit:all 140 | -------------------------------------------------------------------------------- /.github/workflows/dependency_review.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | # Dependency Review Action 4 | # 5 | # This Action will scan dependency manifest files that change as part of a Pull Request, 6 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 7 | # Once installed, if the workflow run is marked as required, 8 | # PRs introducing known-vulnerable packages will be blocked from merging. 9 | # 10 | # Source repository: https://github.com/actions/dependency-review-action 11 | --- 12 | name: 🕵️ Dependency Review 13 | 14 | on: 15 | pull_request: 16 | 17 | permissions: 18 | contents: read 19 | pull-requests: write 20 | checks: write 21 | 22 | jobs: 23 | dependency_review: 24 | name: Dependency Review 25 | runs-on: ubuntu-24.04 26 | steps: 27 | - name: ⤵️ Checkout 28 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 29 | 30 | - name: 🕵️ Run Dependency Review 31 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 32 | -------------------------------------------------------------------------------- /.github/workflows/semantic_pr.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | # Linter to enforce semantic pull request titles (see https://www.conventionalcommits.org/) 4 | --- 5 | name: 🔍 Semantic PR 6 | 7 | on: 8 | pull_request_target: 9 | branches: ["main"] 10 | types: 11 | - opened 12 | - edited 13 | - reopened 14 | - synchronize 15 | - ready_for_review 16 | 17 | permissions: 18 | pull-requests: write 19 | statuses: write 20 | 21 | jobs: 22 | semantic_pr: 23 | name: Semantic PR 24 | runs-on: ubuntu-24.04 25 | steps: 26 | - name: 🔍 Run Semantic PR validation 27 | uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3 28 | id: check_pr_title 29 | with: 30 | wip: true 31 | env: 32 | GITHUB_TOKEN: ${{ github.token }} 33 | 34 | - name: 💬 Comment on PR 35 | if: always() && (steps.check_pr_title.outputs.error_message != null) 36 | uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db # v2.9.2 37 | with: 38 | header: pr-title-check-error 39 | message: | 40 | Hey there and thank you for opening this pull request! 👋🏼 41 | 42 | We require pull request titles to follow the [Conventional Commits](https://www.conventionalcommits.org/) specification and it looks like your proposed title needs to be adjusted. 43 | 44 | Details: 45 | 46 | ```text 47 | ${{ steps.check_pr_title.outputs.error_message }} 48 | ``` 49 | 50 | - name: 🗑 Delete PR comment 51 | if: ${{ steps.check_pr_title.outputs.error_message == null }} 52 | uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db # v2.9.2 53 | with: 54 | header: pr-title-check-error 55 | delete: true 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | **/.external_modules/* 4 | 5 | # .tfstate files 6 | *.tfstate 7 | *.tfstate.* 8 | 9 | # Crash log files 10 | crash.log 11 | crash.*.log 12 | 13 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 14 | # password, private keys, and other secrets. These should not be part of version 15 | # control as they are data points which are potentially sensitive and subject 16 | # to change depending on the environment. 17 | *.tfvars 18 | *.tfvars.json 19 | 20 | # Ignore override files as they are usually used to override resources locally and so 21 | # are not checked in 22 | override.tf 23 | override.tf.json 24 | *_override.tf 25 | *_override.tf.json 26 | 27 | # Ignore transient lock info files created by terraform apply 28 | .terraform.tfstate.lock.info 29 | 30 | # Include override files you do wish to add to version control using negated pattern 31 | # !example_override.tf 32 | 33 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 34 | # example: *tfplan* 35 | 36 | # Ignore CLI configuration files 37 | .terraformrc 38 | terraform.rc 39 | 40 | # Ignore lock files 41 | *.lock.hcl 42 | *.lock.info 43 | 44 | # Ignore cache files 45 | .lycheecache 46 | 47 | # Ignore test result files 48 | *test*-results*.xml 49 | 50 | **/.task/* 51 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | title = "Gitleaks config" 2 | 3 | [extend] 4 | # useDefault will extend the base configuration with the default gitleaks config: 5 | # https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml 6 | useDefault = true 7 | 8 | # rules based on: https://www.powershellgallery.com/packages/AzSK.AzureDevOps/0.9.9/Content/Framework%5CConfigurations%5CSVT%5CAzureDevOps%5CCredentialPatterns.xml 9 | [[rules]] 10 | id = "CSCAN0210" 11 | description = "GitCredential" 12 | regex = '''https?://.+:.+@.*''' 13 | path = '''\.gitCredentials$''' 14 | 15 | [[rules]] 16 | id = "CSCAN0010" 17 | description = "KeyStoreFile" 18 | regex = '''.''' 19 | path = '''\.keystore$''' 20 | 21 | [[rules]] 22 | id = "CSCAN0020-1" 23 | description = "Base64EncodedCertificateInCode" 24 | regex = '''['">;=]MII[a-z0-9/+]{200}''' 25 | path = '''\.(?:cs|ini|json|ps1|publishsettings|template|trd|ts|xml)$''' 26 | 27 | [[rules]] 28 | id = "CSCAN0020-2" 29 | description = "Base64EncodedCertificateInFile" 30 | regex = '''MII[A-Za-z0-9/+]{60}''' 31 | path = '''\.(?:cert|cer)$''' 32 | 33 | [[rules]] 34 | id = "CSCAN0030" 35 | description = "PublishSettings" 36 | regex = '''userPWD="[a-zA-Z0-9\+\/]{60}"''' 37 | path = '''(?i)(publishsettings|\.pubxml$)''' 38 | [rules.allowlists] 39 | regexes = [ 40 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 41 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 42 | ] 43 | 44 | [[rules]] 45 | id = "CSCAN0060" 46 | description = "PemFile" 47 | path = '''\.pem$''' 48 | regex = '''-{5}BEGIN(?: (?:[dr]sa|ec|openssh))? PRIVATE KEY-{5}''' 49 | 50 | [[rules]] 51 | id = "CSCAN0091-1" 52 | description = "AspNetMachineKeyInConfig1" 53 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 54 | regex = ''']+(?:decryptionKey\s*\=\s*"[a-fA-F0-9]{48,}|validationKey\s*\=\s*"[a-fA-F0-9]{48,})[^>]+>''' 55 | [rules.allowlists] 56 | regexes = [ 57 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 58 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 59 | ] 60 | 61 | [[rules]] 62 | id = "CSCAN0091-2" 63 | description = "AspNetMachineKeyInConfig2" 64 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 65 | regex = '''(?:decryptionKey|validationKey)="[a-zA-Z0-9]+"''' 66 | [rules.allowlists] 67 | regexes = [ 68 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 69 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 70 | ] 71 | 72 | [[rules]] 73 | id = "CSCAN0092-1" 74 | description = "SqlConnectionStringInConfig1" 75 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 76 | regex = '''(?i)(?:connection[sS]tring|connString)[^=]*=["'][^"']*[pP]assword\s*=\s*[^\s;][^"']*(?:'|")''' 77 | [rules.allowlists] 78 | regexes = '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''' 79 | 80 | [[rules]] 81 | id = "CSCAN0092-2" 82 | description = "SqlConnectionStringInConfig2" 83 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties|policy_and_key\.hpp|AccountConfig\.h)$|hubot''' 84 | regex = '''(?i)(?:User ID|uid|UserId).*(?:Password|[^a-z]pwd)=[^'\$%<@'";\[\{][^;/"]{4,128}(?:;|")''' 85 | [rules.allowlists] 86 | regexes = [ 87 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 88 | '''(?:prefix <<|guestaccesstoken|skiptoken|cookie|tsm|fake|example|badlyFormatted|Invalid|sha512|sha256|"input"|ENCRYPTED|"EncodedRequestUri"|looks like|myStorageAccountName|(?:0|x|\*){8,})''', 89 | ] 90 | 91 | [[rules]] 92 | id = "CSCAN0043" 93 | description = "SqlConnectionStringInCode" 94 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties|policy_and_key\.hpp|AccountConfig\.h)$|hubot''' 95 | regex = '''(?i)(?:User ID|uid|UserId).*(?:Password|[^a-z]pwd)=[^'\$%<@'";\[\{][^;/"]{4,128}(?:;|")''' 96 | [rules.allowlists] 97 | regexes = [ 98 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 99 | '''(?:prefix <<|guestaccesstoken|skiptoken|cookie|tsm|fake|example|badlyFormatted|Invalid|sha512|sha256|"input"|ENCRYPTED|"EncodedRequestUri"|looks like|myStorageAccountName|(?:0|x|\*){8,})''', 100 | ] 101 | 102 | [[rules]] 103 | id = "CSCAN0093" 104 | description = "StorageAccountKeyInConfig" 105 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 106 | regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{86}==''' 107 | 108 | [[rules]] 109 | id = "CSCAN0041" 110 | description = "StorageAccountKeyInCode" 111 | path = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' 112 | regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{86}==''' 113 | 114 | [[rules]] 115 | id = "CSCAN0094-1" 116 | description = "SharedAccessSignatureInCode1" 117 | path = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' 118 | regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{43}=[^{@]''' 119 | 120 | [[rules]] 121 | id = "CSCAN0094-2" 122 | description = "SharedAccessSignatureInCode2" 123 | path = '''(?:\.(?:cs|js|ts|cpp)|policy_and_key\.hpp|AccountConfig\.h)$''' 124 | regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9%]{43,53}%3d[^a-z0-9%]''' 125 | 126 | [[rules]] 127 | id = "CSCAN0094-3" 128 | description = "SharedAccessSignatureInConfig1" 129 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 130 | regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9/+]{43}=[^{@]''' 131 | 132 | [[rules]] 133 | id = "CSCAN0094-4" 134 | description = "SharedAccessSignatureInConfig2" 135 | path = '''\.(?:xml|pubxml|definitions|ps1|wadcfgx|ccf|config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 136 | regex = '''[^a-z0-9/\+\._\-\$,\\][a-z0-9%]{43,53}%3d[^a-z0-9%]''' 137 | 138 | [[rules]] 139 | id = "CSCAN0095-1" 140 | description = "GeneralSecretInConfig1" 141 | path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 142 | regex = ''']*/>''' 143 | [rules.allowlists] 144 | regexes = [ 145 | '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''', 146 | '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''', 147 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 148 | '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''', 149 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 150 | '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''', 151 | ] 152 | 153 | [[rules]] 154 | id = "CSCAN0095-2" 155 | description = "GeneralSecretInConfig2" 156 | path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 157 | regex = '''''' 158 | [rules.allowlists] 159 | regexes = [ 160 | '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''', 161 | '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''', 162 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 163 | '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''', 164 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 165 | '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''', 166 | ] 167 | 168 | [[rules]] 169 | id = "CSCAN0095-3" 170 | description = "GeneralSecretInConfig3" 171 | path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 172 | regex = '''|[^>]*>.*?)''' 173 | [rules.allowlists] 174 | regexes = [ 175 | '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''', 176 | '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''', 177 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 178 | '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''', 179 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 180 | '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''', 181 | ] 182 | 183 | [[rules]] 184 | id = "CSCAN0095-4" 185 | description = "GeneralSecretInConfig4" 186 | path = '''\.(?:config|cscfg|json|js|txt|cpp|sql|dtsx|md|java|FF|template|settings|ini|BF|ste|isml|test|ts|resx|Azure|sample|backup|rd|hpp|psm1|cshtml|htm|bat|waz|yml|Beta|py|sh|m|php|xaml|keys|cmd|rds|loadtest|properties)$|hubot''' 187 | regex = '''.+''' 188 | [rules.allowlists] 189 | regexes = [ 190 | '''key\s*=\s*"[^"]*AppKey[^"]*"\s+value\s*=\s*"[a-z]+"''', 191 | '''value\s*=\s*"(?:[a-z]+(?: [a-z]+)+"|_+[a-z]+_+"|[a-z]+-[a-z]+-[a-z]+["-]|[a-z]+-[a-z]+"|[a-z]+\\[a-z]+"|\d+"|[^"]*ConnectionString")''', 192 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 193 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 194 | '''value="(?:true|false|@\(api|ssh\-rsa 2048|invalid|to be|a shared secret|secreturi|clientsecret|Overr?idden by|someValue|SOME\-SIGNING\-KEY|TokenBroker|UNKNOWN|Client Secret of|Junk Credentials|Default\-|__BOOTSTRAPKEY_|CacheSecret|CatalogCert|CosmosCredentials|DeleteServiceCert|EmailCredentials|MetricsConnection|SangamCredentials|SubscriptionConnection|Enter_your_|My_Issuer|ScaleUnitXstoreSharedKey|private_powerapps|TestSecret|foo_|bar_|temp_|__WinfabricTestInfra|configured|SecretFor|Test|XSTORE_KEY|ServiceBusDiagnosticXstoreSharedKey|BoxApplicationKey|googleapps)''', 195 | '''AccountKey\s*=\s*MII[a-z0-9/+]{43,}={0,2}''', 196 | ] 197 | 198 | [[rules]] 199 | id = "CSCAN0110-1" 200 | description = "ScriptPassword1" 201 | path = '''(?:\.cmd|\.ps|\.ps1|\.psm1)$''' 202 | regex = '''\s-Password\s+(?:"[^"]*"|'[^']*')''' 203 | 204 | [[rules]] 205 | id = "CSCAN0110-2" 206 | description = "ScriptPassword2" 207 | path = '''(?:\.cmd|\.ps|\.ps1|\.psm1)$''' 208 | regex = '''\s-Password\s+[^$\(\)\[\{<\-\r?\n]+\s*(?:\r?\n|\-)''' 209 | 210 | [[rules]] 211 | id = "CSCAN0120" 212 | description = "ExternalApiSecret" 213 | path = '''\.cs$|\.cpp$|\.c$''' 214 | regex = '''(private\sconst\sstring\sAccessTokenSecret|private\sconst\sstring\saccessToken|private\sconst\sstring\sconsumerSecret|private\sconst\sstring\sconsumerKey|pageAccessToken|private\sstring\stwilioAccountSid|private\sstring\stwilioAuthToken)\s=\s".*";''' 215 | 216 | [[rules]] 217 | id = "CSCAN0220-1" 218 | description = "DefaultPasswordContexts1" 219 | path = '''\.(?:ps1|psm1|)$''' 220 | regex = '''ConvertTo-SecureString(?:\s*-String)?\s*"[^$"\r?\n]+"''' 221 | [rules.allowlists] 222 | regexes = [ 223 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 224 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 225 | ] 226 | 227 | [[rules]] 228 | id = "CSCAN0220-2" 229 | description = "DefaultPasswordContexts2" 230 | path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' 231 | regex = '''new\sX509Certificate2\([^()]*,\s*"[^"\r?\n]+"[^)]*\)''' 232 | [rules.allowlists] 233 | regexes = [ 234 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 235 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 236 | ] 237 | 238 | [[rules]] 239 | id = "CSCAN0220-3" 240 | description = "DefaultPasswordContexts3" 241 | path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' 242 | regex = '''AdminPassword\s*=\s*"[^"\r?\n]+"''' 243 | [rules.allowlists] 244 | regexes = [ 245 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 246 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 247 | ] 248 | 249 | [[rules]] 250 | id = "CSCAN0220-4" 251 | description = "DefaultPasswordContexts4" 252 | path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' 253 | regex = '''(?i).+''' 254 | [rules.allowlists] 255 | regexes = [ 256 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 257 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 258 | ] 259 | 260 | [[rules]] 261 | id = "CSCAN0220-5" 262 | description = "DefaultPasswordContexts5" 263 | path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' 264 | regex = '''ClearTextPassword"?\s*[:=]\s*"[^"\r?\n]+"''' 265 | [rules.allowlists] 266 | regexes = [ 267 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 268 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 269 | ] 270 | 271 | [[rules]] 272 | id = "CSCAN0220-6" 273 | description = "DefaultPasswordContexts6" 274 | path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' 275 | regex = '''certutil.*?\-p\s+("[^"%]+"|'[^'%]+'|[^"']\S*\s)''' 276 | [rules.allowlists] 277 | regexes = [ 278 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 279 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 280 | ] 281 | 282 | [[rules]] 283 | id = "CSCAN0220-7" 284 | description = "DefaultPasswordContexts7" 285 | path = '''\.(?:cs|xml|config|json|ts|cfg|txt|ps1|bat|cscfg|publishsettings|cmd|psm1|aspx|asmx|vbs|added_cluster|clean|pubxml|ccf|ini|svd|sql|c|xslt|csv|FF|ExtendedTests|settings|cshtml|template|trd|argpath)$|(config|certificate|publish|UT)\.js$|(commands|user|tests)\.cpp$''' 286 | regex = '''password\s*=\s*N?(["][^"\r?\n]{4,}["]|['][^'\r?\n]{4,}['])''' 287 | [rules.allowlists] 288 | regexes = [ 289 | '''Credentials?Type|ConnectionStringKey|notasecret|PartitionKey|notreal|insertkey|LookupKey|IgnoreKeys|SecretsService|SecretsTenantId|(?:Password|pwd|secret|credentials?)(?:Key|Location)|KeyManager''', 290 | '''(?:_AppKey"|(?:(?:credential|password|token)s?|(?:Account|access)Key=)"[\s\r?\n]*/|Username"|\.dll|(?:Secret|Token|Key|Credential)s?(?:Encryption|From|(?:Signing)?Certificate|Options|Thumbprint|Contacts|String|UserId)|Key(1;value1|word|s?Path|Index|Id|Store|WillDoWithoutValidation|:NamePattern|Name"|Ref")|(Secret|Credential)s?(Name|Path)"|(StrongName|Chaos\s?Mon|Redis|Registry|Registery|User|Insights?|Instrumentation|Match\()Key|(Certificate|cert)(Issuer|Subject)|rollingdate|skuId|HKEY_|AddServicePrincipalCredentials|Password Resets|SecretStore|(0|x|\*){8,})''', 291 | ] 292 | 293 | [[rules]] 294 | id = "CSCAN0160" 295 | description = "DomainPassword" 296 | regex = '''new(?:-object)?\s+System.Net.NetworkCredential\(?:.*?,\s*"[^"]+"''' 297 | path = '''\.cs$|\.c$|\.cpp$|\.ps1$|\.ps$|\.cmd$|\.bat$|\.log$|\.psd$|\.psm1$''' 298 | [rules.allowlists] 299 | regexes = '''(%1%|\$MIGUSER_PASSWORD|%miguser_pwd%)''' 300 | description = "ignore placeholders" 301 | 302 | [[rules]] 303 | id = "CSCAN0240-1" 304 | description = "VstsPersonalAccessToken1" 305 | path = '''\.(?:cs|ps1|bat|config|xml|json|md|yml|yaml)$''' 306 | regex = '''(?i)(?:AccessToken|pat|token).*?[':="][a-z0-9]{52}(?:'|"|\s|[\r?\n]+)''' 307 | 308 | [[rules]] 309 | d = "CSCAN0240-2" 310 | description = "VstsPersonalAccessToken2" 311 | path = '''\.(?:cs|ps1|bat|config|xml|json|md|yml|yaml)$''' 312 | regex = '''(?i)(?:AccessToken|pat|token).*?[':="][a-z0-9/+]{70}==(?:'|"|\s|[\r?\n]+)''' 313 | 314 | [[rules]] 315 | id = "CSCAN0250-1" 316 | description = "OauthToken1" 317 | path = '''\.(?:config|js|json|txt|cs|xml|java|py)$''' 318 | regex = '''eyj[a-z0-9\-_%]+\.eyj[a-z0-9\-_%]+\.[a-z0-9\-_%]+''' 319 | 320 | [[rules]] 321 | id = "CSCAN0250-2" 322 | description = "OauthToken2" 323 | path = '''\.(?:config|js|json|txt|cs|xml|java|py)$''' 324 | regex = '''refresh_token["']?\s*[:=]\s*["']?(?:[a-z0-9_]+-)+[a-z0-9_]+["']?''' 325 | 326 | [[rules]] 327 | id = "CSCAN0260" 328 | description = "AnsibleVault" 329 | path = '''\.yml$''' 330 | regex = '''\$ANSIBLE_VAULT;[0-9]\.[0-9];AES256[\r?\n]+[0-9]+''' 331 | 332 | [[rules]] 333 | id = "CSCAN0230-1" 334 | description = "SlackToken1" 335 | regex = '''xoxp-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+-[a-z0-9]+''' 336 | path = '''\.(?:ps1|psm1|js|json|coffee|xml|js|md|html|py|php|java|ipynb|rb)$|hubot''' 337 | 338 | [[rules]] 339 | id = "CSCAN0230-2" 340 | description = "SlackToken2" 341 | regex = '''xoxb-[a-z0-9]+-[a-z0-9]+''' 342 | path = '''\.(?:ps1|psm1|js|json|coffee|xml|js|md|html|py|php|java|ipynb|rb)$|hubot''' 343 | 344 | [allowlist] 345 | description = "Allowlisted files" 346 | paths = [ 347 | '''(.*?)(png|tif|tiff|pyc)$''', 348 | '''buildsearchers.xml''', 349 | '''UDMSecretChecks.toml''', 350 | '''UDMSecretChecksv8.toml''', 351 | '''GitleaksUdmCombo.toml''', 352 | '''.github/linters''', 353 | '''node_modules''', 354 | '''(.*?)gitleaks\.toml$''', 355 | ] 356 | commits = [] 357 | repos = [] 358 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/golangci-lint.json 2 | # Visit https://golangci-lint.run/ for usage documentation and information on other useful linters 3 | --- 4 | version: "2" 5 | run: 6 | allow-parallel-runners: true 7 | allow-serial-runners: true 8 | output: 9 | sort-order: 10 | - linter 11 | - severity 12 | - file 13 | linters: 14 | default: all 15 | disable: 16 | - cyclop 17 | - depguard 18 | - err113 19 | - exhaustruct 20 | - funlen 21 | - goheader 22 | - gomoddirectives 23 | - ireturn 24 | - lll 25 | - mnd 26 | - paralleltest 27 | - wrapcheck 28 | settings: 29 | exhaustive: 30 | default-signifies-exhaustive: true 31 | godot: 32 | capital: true 33 | nestif: 34 | min-complexity: 10 35 | revive: 36 | enable-all-rules: true 37 | rules: 38 | - name: line-length-limit 39 | disabled: true 40 | - name: cognitive-complexity 41 | disabled: true 42 | - name: add-constant 43 | disabled: true 44 | - name: bare-return 45 | disabled: true 46 | - name: function-length 47 | disabled: true 48 | - name: unused-receiver 49 | disabled: true 50 | - name: cyclomatic 51 | disabled: true 52 | - name: if-return 53 | disabled: true 54 | - name: max-public-structs 55 | disabled: true 56 | - name: confusing-naming 57 | disabled: true 58 | varnamelen: 59 | min-name-length: 1 60 | wsl: 61 | allow-trailing-comment: true 62 | allow-cuddle-declarations: true 63 | exclusions: 64 | generated: lax 65 | presets: 66 | - comments 67 | - common-false-positives 68 | - legacy 69 | - std-error-handling 70 | rules: 71 | - path: (.+)_test\.go 72 | text: unchecked-type-assertion 73 | - linters: 74 | - dupl 75 | - forcetypeassert 76 | - gochecknoglobals 77 | - gocognit 78 | - goconst 79 | - nonamedreturns 80 | path: (.+)_test\.go 81 | paths: 82 | - third_party$ 83 | - builtin$ 84 | - examples$ 85 | issues: 86 | max-issues-per-linter: 0 87 | max-same-issues: 0 88 | fix: true 89 | formatters: 90 | enable: 91 | - gci 92 | - gofmt 93 | - gofumpt 94 | - goimports 95 | - golines 96 | settings: 97 | gci: 98 | sections: 99 | - standard 100 | - default 101 | - localmodule 102 | golines: 103 | max-len: 200 104 | exclusions: 105 | generated: lax 106 | paths: 107 | - third_party$ 108 | - builtin$ 109 | - examples$ 110 | -------------------------------------------------------------------------------- /.lychee.toml: -------------------------------------------------------------------------------- 1 | # https://lychee.cli.rs/#/usage/config 2 | # Example config: https://github.com/lycheeverse/lychee/blob/master/lychee.example.toml 3 | 4 | 5 | ############################# Cache ############################### 6 | 7 | # Enable link caching. This can be helpful to avoid checking the same links on multiple runs. 8 | cache = true 9 | 10 | # Discard all cached requests older than this duration. 11 | max_cache_age = "1d" 12 | 13 | ############################# Runtime ############################# 14 | 15 | # Maximum number of allowed redirects. 16 | max_redirects = 6 17 | 18 | # Maximum number of allowed retries before a link is declared dead. 19 | max_retries = 2 20 | 21 | # Maximum number of concurrent link checks. 22 | # max_concurrency = 2 23 | 24 | ############################# Requests ############################ 25 | 26 | # User agent to send with each request. 27 | user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.0.0" 28 | 29 | # Website timeout from connect to response finished. 30 | timeout = 45 31 | 32 | # Minimum wait time in seconds between retries of failed requests. 33 | retry_wait_time = 2 34 | 35 | # Comma-separated list of accepted status codes for valid links. 36 | accept = ["200", "206", "301", "429"] 37 | 38 | # Only test links with the given schemes (e.g. https). 39 | # Omit to check links with any scheme. 40 | scheme = ["https", "http", "file"] 41 | 42 | # Custom request headers 43 | headers = ['Accept-Encoding: deflate, compress, gzip, br, zstd'] 44 | 45 | ############################# Exclusions ########################## 46 | 47 | # Ignore case of paths when matching glob patterns. 48 | glob_ignore_case = true 49 | 50 | # Exclude all private IPs from checking. 51 | exclude_all_private = true 52 | 53 | # Exclude private IP address ranges from checking. 54 | exclude_private = true 55 | 56 | # Exclude link-local IP address range from checking. 57 | exclude_link_local = true 58 | 59 | # Exclude loopback IP address range and localhost from checking. 60 | exclude_loopback = true 61 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/main/schema/markdownlint-cli2-config-schema.json 2 | --- 3 | config: 4 | extends: ./.markdownlint.yml 5 | gitignore: true 6 | # ignores: 7 | # - ./.changes/*.md 8 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/DavidAnson/markdownlint/main/schema/markdownlint-config-schema.json 2 | --- 3 | # Markdownlint YAML configuration 4 | # Default source: https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml 5 | 6 | # Default state for all rules 7 | default: true 8 | 9 | # Path to configuration file to extend 10 | # extends: null 11 | 12 | # MD013/line-length - Line length 13 | MD013: false 14 | 15 | # MD024/no-duplicate-heading 16 | MD024: false 17 | 18 | # MD025/single-title - Single title 19 | MD025: 20 | front_matter_title: "" 21 | 22 | # MD033/no-inline-html - Inline HTML 23 | MD033: 24 | # Allowed elements 25 | allowed_elements: ["br", "pre"] 26 | 27 | # MD041/first-line-heading/first-line-h1 28 | MD041: false 29 | 30 | # MD046/code-block-style - Code block style 31 | MD046: 32 | # Block style 33 | style: "fenced" 34 | -------------------------------------------------------------------------------- /.shellcheckrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/fabric-terraform-quickstart/9e5a4d193115679509b8bbdfeaa5e0b841ee3660/.shellcheckrc -------------------------------------------------------------------------------- /.taskfiles/github.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install Actionlint 11 | install:actionlint: 12 | desc: Install Actionlint 13 | cmds: 14 | - task: internal:install:winget 15 | vars: 16 | APP: "rhysd.actionlint" 17 | - task: internal:install:brew 18 | vars: 19 | APP: actionlint 20 | - cmd: | 21 | curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash | sudo bash -s -- latest /usr/local/bin 22 | platforms: [linux] 23 | 24 | # * Install ACT 25 | install:act: 26 | desc: Install ACT 27 | cmds: 28 | - task: internal:install:winget 29 | vars: 30 | APP: "nektos.act" 31 | - task: internal:install:brew 32 | vars: 33 | APP: act 34 | - cmd: | 35 | curl --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash -s -- -b /usr/local/bin 36 | platforms: [linux] 37 | 38 | # * Install GitHub CLI 39 | install:gh: 40 | desc: Install GitHub CLI 41 | cmds: 42 | - task: internal:install:winget 43 | vars: 44 | APP: "GitHub.cli" 45 | - task: internal:install:brew 46 | vars: 47 | APP: gh 48 | - cmd: | 49 | (type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) \ 50 | && sudo mkdir -p -m 755 /etc/apt/keyrings \ 51 | && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \ 52 | && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \ 53 | && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \ 54 | && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \ 55 | && sudo apt update \ 56 | && sudo apt install gh -y 57 | platforms: [linux] 58 | 59 | # * Tools 60 | tools: 61 | desc: Install GitHub tools 62 | cmds: 63 | - task: install:actionlint 64 | - task: install:act 65 | - task: install:gh 66 | 67 | # * Lint 68 | lint: 69 | desc: Lint GitHub files 70 | cmds: 71 | - actionlint 72 | dir: "{{.ROOT_DIR}}" 73 | -------------------------------------------------------------------------------- /.taskfiles/golang.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install golangci-lint 11 | install:golangci-lint: 12 | desc: Install golangci-lint 13 | cmds: 14 | - task: internal:install:winget 15 | vars: 16 | APP: "GolangCI.golangci-lint" 17 | - task: internal:install:brew 18 | vars: 19 | APP: golangci-lint 20 | - cmd: | 21 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "$(go env GOPATH)/bin" latest 22 | platforms: [linux] 23 | 24 | # * Install govulncheck 25 | install:govulncheck: 26 | desc: Install govulncheck 27 | cmds: 28 | - task: internal:install:go 29 | vars: 30 | APP: "golang.org/x/vuln/cmd/govulncheck@latest" 31 | 32 | # * Tools 33 | tools: 34 | desc: Install GoLang tools 35 | cmds: 36 | - task: install:golangci-lint 37 | - task: install:govulncheck 38 | 39 | # * Lint 40 | lint: 41 | desc: Run GoLang linters 42 | vars: 43 | WORKDIRS: 44 | sh: | 45 | {{if eq OS "windows"}} 46 | {{.PWSH}} '(Get-ChildItem . go.mod -Recurse).DirectoryName | Where-Object {$_ -notmatch "\\(vendor|.terraform)\\" } | Select-Object -Unique | ForEach-Object { Resolve-Path -Relative $_ }' 47 | {{else}} 48 | find . -mindepth 2 -not -path "*/vendor/*" -not -path "*/.terraform/*" -type f -name go.mod -exec dirname {} \; 49 | {{end}} 50 | cmds: 51 | - for: { var: WORKDIRS } 52 | task: run:golangci 53 | vars: 54 | WORKDIR: "{{.ITEM}}" 55 | - for: { var: WORKDIRS } 56 | task: run:govulncheck 57 | vars: 58 | WORKDIR: "{{.ITEM}}" 59 | dir: "{{.ROOT_DIR}}" 60 | 61 | run:golangci: 62 | desc: Run golangci-lint 63 | internal: true 64 | preconditions: 65 | - sh: | 66 | {{empty .WORKDIR | not}} 67 | msg: No WORKDIR provided. Please provide a WORKDIR to check. 68 | cmds: 69 | - golangci-lint run --fix 70 | dir: "{{.WORKDIR}}" 71 | 72 | run:govulncheck: 73 | desc: govulncheck 74 | internal: true 75 | preconditions: 76 | - sh: | 77 | {{empty .WORKDIR | not}} 78 | msg: No WORKDIR provided. Please provide a WORKDIR to check. 79 | cmds: 80 | - govulncheck -test -show verbose ./... 81 | dir: "{{.WORKDIR}}" 82 | -------------------------------------------------------------------------------- /.taskfiles/internal.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | vars: 7 | PWSH: pwsh -NonInteractive -NoProfile -NoLogo -Command 8 | PWSH_SCRIPT: pwsh -NonInteractive -NoProfile -NoLogo -File 9 | 10 | tasks: 11 | command: 12 | desc: Check if command exists 13 | silent: true 14 | preconditions: 15 | - sh: | 16 | {{empty .CLI_ARGS | not}} 17 | msg: No command provided. Please provide a command to check. 18 | cmds: 19 | - cmd: command -v "{{.CLI_ARGS}}" 20 | platforms: [linux, darwin] 21 | - cmd: | 22 | {{.PWSH}} 'Get-Command "{{.CLI_ARGS}}"' 23 | platforms: [windows] 24 | 25 | install:go: 26 | desc: go install 27 | internal: true 28 | preconditions: 29 | - sh: task internal:command -- go 30 | msg: "go is not installed. Please install go: https://go.dev/doc/install" 31 | cmds: 32 | - go install {{.APP}} 33 | 34 | install:winget: 35 | desc: winget install 36 | internal: true 37 | preconditions: 38 | - sh: task internal:command -- winget 39 | msg: "winget is not installed. Please install winget: https://learn.microsoft.com/windows/package-manager/winget/" 40 | cmds: 41 | - winget install {{.APP}} --accept-source-agreements --accept-package-agreements 42 | ignore_error: true 43 | platforms: [windows] 44 | 45 | install:brew: 46 | desc: brew install 47 | internal: true 48 | preconditions: 49 | - sh: task internal:command -- brew 50 | msg: "brew is not installed. Please install brew." 51 | cmds: 52 | - brew install {{.APP}} 53 | platforms: [darwin] 54 | 55 | install:brew:tap: 56 | desc: brew tap 57 | internal: true 58 | preconditions: 59 | - sh: task internal:command -- brew 60 | msg: "brew is not installed. Please install brew." 61 | cmds: 62 | - brew tap {{.APP}} 63 | platforms: [darwin] 64 | 65 | install:pipx: 66 | desc: pipx install 67 | internal: true 68 | preconditions: 69 | - sh: task internal:command -- pipx 70 | msg: "pipx is not installed. Please install pipx: https://pipx.pypa.io/" 71 | cmds: 72 | - pipx install --include-deps {{.APP}} 73 | 74 | install:npm: 75 | desc: npm install 76 | internal: true 77 | preconditions: 78 | - sh: task internal:command -- npm 79 | msg: "npm is not installed. Please install npm: https://docs.npmjs.com/cli/commands/npm" 80 | cmds: 81 | - npm install --global {{.APP}} 82 | 83 | install:uv: 84 | desc: uv install 85 | internal: true 86 | preconditions: 87 | - sh: task internal:command -- uv 88 | msg: "uv is not installed. Please install uv: https://docs.astral.sh/uv/getting-started/installation/" 89 | cmds: 90 | - uv tool install --force {{.APP}} 91 | -------------------------------------------------------------------------------- /.taskfiles/markdown.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install markdownlint-cli2 11 | install:markdownlint: 12 | desc: Install markdownlint-cli2 13 | cmds: 14 | - task: internal:install:npm 15 | vars: 16 | APP: "markdownlint-cli2" 17 | 18 | # * Install markdown-table-formatter 19 | install:table-formatter: 20 | desc: Install markdown-table-formatter 21 | cmds: 22 | - task: internal:install:npm 23 | vars: 24 | APP: "markdown-table-formatter" 25 | 26 | # * Tools 27 | tools: 28 | desc: Install Markdown tools 29 | cmds: 30 | - task: install:markdownlint 31 | - task: install:table-formatter 32 | 33 | # * Lint 34 | lint: 35 | desc: Lint Markdown files 36 | cmds: 37 | - markdownlint-cli2 "./**/*.md" --config "./.markdownlint-cli2.yaml" --fix 38 | - markdown-table-formatter "./**/*.md" 39 | dir: "{{.ROOT_DIR}}" 40 | -------------------------------------------------------------------------------- /.taskfiles/security.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install Gitleaks 11 | install:gitleaks: 12 | desc: Install Gitleaks 13 | cmds: 14 | - task: internal:install:winget 15 | vars: 16 | APP: "Gitleaks.Gitleaks" 17 | - task: internal:install:brew 18 | vars: 19 | APP: gitleaks 20 | - cmd: | 21 | arch=$(uname -m) 22 | if [[ "${arch}" == "x86_64" ]]; then 23 | arch="x64" 24 | elif [[ "${arch}" == "aarch64" ]]; then 25 | arch="arm64" 26 | else 27 | echo "Unsupported architecture: ${arch}" 28 | exit 1 29 | fi 30 | download_url=$(curl --location --silent "https://api.github.com/repos/gitleaks/gitleaks/releases/latest" | grep 'browser_download_url.*gitleaks.*linux_'"${arch}"'.*tar.gz"' | grep -o 'https://[^"]*') 31 | curl --location -o /tmp/gitleaks.tar.gz "${download_url}" 32 | tar -xf /tmp/gitleaks.tar.gz --directory /tmp 33 | rm /tmp/gitleaks.tar.gz 34 | sudo mv /tmp/gitleaks /usr/local/bin/gitleaks 35 | platforms: [linux] 36 | 37 | # * Install Trivy 38 | install:trivy: 39 | desc: Install Trivy 40 | cmds: 41 | - task: internal:install:winget 42 | vars: 43 | APP: "AquaSecurity.Trivy" 44 | - task: internal:install:brew 45 | vars: 46 | APP: trivy 47 | - cmd: | 48 | sudo apt-get install wget gnupg 49 | wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null 50 | echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list 51 | sudo apt-get update 52 | sudo apt-get install trivy 53 | platforms: [linux] 54 | 55 | # * Install Checkov 56 | install:checkov: 57 | desc: Install Checkov 58 | cmds: 59 | - task: internal:install:pipx 60 | vars: 61 | APP: checkov 62 | 63 | # * Tools 64 | tools: 65 | desc: Install Security tools 66 | cmds: 67 | - task: install:gitleaks 68 | - task: install:trivy 69 | - task: install:checkov 70 | 71 | # * Lint 72 | lint: 73 | desc: Run Security linters 74 | cmds: 75 | - gitleaks detect --config ./.gitleaks.toml --source . --redact=90 --verbose 76 | - trivy --config ./.trivy.yml fs . 77 | - checkov --config-file ./.checkov.yml --directory . 78 | dir: "{{.ROOT_DIR}}" 79 | -------------------------------------------------------------------------------- /.taskfiles/spell.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install CSpell 11 | install:cspell: 12 | desc: Install CSpell 13 | cmds: 14 | - task: internal:install:npm 15 | vars: 16 | APP: cspell 17 | 18 | # * Install CodeSpell 19 | install:codespell: 20 | desc: Install CodeSpell 21 | cmds: 22 | - task: internal:install:pipx 23 | vars: 24 | APP: codespell 25 | 26 | # * Install Lychee 27 | install:lychee: 28 | desc: Install Lychee 29 | cmds: 30 | - task: internal:install:winget 31 | vars: 32 | APP: "lycheeverse.lychee" 33 | - task: internal:install:brew 34 | vars: 35 | APP: lychee 36 | - cmd: | 37 | arch=$(uname -m) 38 | download_url=$(curl --location --silent "https://api.github.com/repos/lycheeverse/lychee/releases/latest" | grep 'browser_download_url.*lychee-'"${arch}"'.*gnu.*tar.gz' | grep -o 'https://[^"]*') 39 | curl --location -o /tmp/lychee.tar.gz "${download_url}" 40 | tar -xf /tmp/lychee.tar.gz --directory /tmp 41 | rm /tmp/lychee.tar.gz 42 | sudo mv /tmp/lychee /usr/local/bin/lychee 43 | platforms: [linux] 44 | 45 | # * Tools 46 | tools: 47 | desc: Install Spell tools 48 | cmds: 49 | - task: install:cspell 50 | - task: install:codespell 51 | - task: install:lychee 52 | 53 | # * Lint 54 | lint: 55 | desc: Lint Spell files 56 | cmds: 57 | - cmd: | 58 | $(pipx environment --value PIPX_BIN_DIR)/codespell --summary --write-changes 59 | platforms: [linux, darwin] 60 | - cmd: | 61 | codespell --summary --write-changes 62 | platforms: [windows] 63 | - cspell --config ./.cspell.yml "./**/*.md" 64 | - lychee --config ./.lychee.toml --format markdown . 65 | dir: "{{.ROOT_DIR}}" 66 | -------------------------------------------------------------------------------- /.taskfiles/terraform.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install Terraform 11 | install:terraform: 12 | desc: Install Terraform 13 | cmds: 14 | - task: internal:install:winget 15 | vars: 16 | APP: "HashiCorp.Terraform" 17 | - task: internal:install:brew:tap 18 | vars: 19 | APP: "hashicorp/tap" 20 | - task: internal:install:brew 21 | vars: 22 | APP: "hashicorp/tap/terraform" 23 | - cmd: | 24 | wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --batch --yes --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg 25 | echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list 26 | sudo apt update && sudo apt install terraform 27 | platforms: [linux] 28 | 29 | # * Install OpenTofu 30 | install:opentofu: 31 | desc: Install OpenTofu 32 | cmds: 33 | - task: internal:install:winget 34 | vars: 35 | APP: "OpenTofu.Tofu" 36 | - task: internal:install:brew 37 | vars: 38 | APP: "opentofu" 39 | - cmd: | 40 | curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh 41 | chmod +x install-opentofu.sh 42 | ./install-opentofu.sh --install-method deb 43 | rm -f install-opentofu.sh 44 | platforms: [linux] 45 | 46 | # * Install Terraform-Docs 47 | install:terraform-docs: 48 | desc: Install Terraform-Docs 49 | cmds: 50 | - task: internal:install:winget 51 | vars: 52 | APP: "Terraform-docs.Terraform-docs" 53 | - task: internal:install:brew 54 | vars: 55 | APP: "terraform-docs" 56 | - cmd: | 57 | arch=$(uname -m) 58 | if [[ "${arch}" == "x86_64" ]]; then 59 | arch="amd64" 60 | elif [[ "${arch}" == "aarch64" ]]; then 61 | arch="arm64" 62 | else 63 | echo "Unsupported architecture: ${arch}" 64 | exit 1 65 | fi 66 | download_url=$(curl --location --silent "https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest" | grep 'browser_download_url.*terraform-docs.*linux-'"${arch}"'.*tar.gz"' | grep -o 'https://[^"]*') 67 | curl --location -o /tmp/terraform-docs.tar.gz "${download_url}" 68 | tar -xf /tmp/terraform-docs.tar.gz --directory /tmp 69 | rm /tmp/terraform-docs.tar.gz 70 | chmod +x /tmp/terraform-docs 71 | sudo mv /tmp/terraform-docs /usr/local/bin/terraform-docs 72 | platforms: [linux] 73 | 74 | # * Install TFLint 75 | install:tflint: 76 | desc: Install TFLint 77 | cmds: 78 | - task: internal:install:winget 79 | vars: 80 | APP: "TerraformLinters.TFLint" 81 | - task: internal:install:brew 82 | vars: 83 | APP: "tflint" 84 | - cmd: | 85 | curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash 86 | platforms: [linux] 87 | 88 | # * Install Terrascan 89 | install:terrascan: 90 | desc: Install Terrascan 91 | cmds: 92 | - cmd: | 93 | {{.PWSH}} 'Invoke-WebRequest -Uri ((Invoke-RestMethod https://api.github.com/repos/tenable/terrascan/releases/latest).assets | Where-Object { $_.browser_download_url -match "Windows_x86_64\.tar\.gz$" } | Select-Object -ExpandProperty browser_download_url) -OutFile "$env:TMP\terrascan.tar.gz"' 94 | {{.PWSH}} 'tar -xzf "$env:TMP\terrascan.tar.gz" -C "$env:TMP" terrascan.exe' 95 | {{.PWSH}} 'Remove-Item "$env:TMP\terrascan.tar.gz" -Force' 96 | platforms: [windows] 97 | - cmd: | 98 | curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Darwin_x86_64.tar.gz")" > /tmp/terrascan.tar.gz 99 | tar -xf /tmp/terrascan.tar.gz --directory /tmp terrascan && rm /tmp/terrascan.tar.gz 100 | sudo install /tmp/terrascan /usr/local/bin/terrascan && rm /tmp/terrascan 101 | platforms: [darwin] 102 | - cmd: | 103 | curl -L "$(curl -s https://api.github.com/repos/tenable/terrascan/releases/latest | grep -o -E "https://.+?_Linux_x86_64.tar.gz")" > /tmp/terrascan.tar.gz 104 | tar -xf /tmp/terrascan.tar.gz --directory /tmp terrascan && rm /tmp/terrascan.tar.gz 105 | sudo install /tmp/terrascan /usr/local/bin/terrascan && rm /tmp/terrascan 106 | platforms: [linux] 107 | 108 | # * Tools 109 | tools: 110 | desc: Install Terraform tools 111 | cmds: 112 | - task: install:terraform 113 | - task: install:opentofu 114 | - task: install:terraform-docs 115 | - task: install:tflint 116 | # - task: install:terrascan 117 | 118 | # * Lint 119 | lint: 120 | desc: Run Terraform linters 121 | cmds: 122 | - terraform fmt -recursive 123 | - tflint --init --config "$(pwd)/.tflint.hcl" 124 | - tflint --recursive --fix --config "$(pwd)/.tflint.hcl" 125 | dir: "{{.ROOT_DIR}}" 126 | 127 | # * Docs 128 | docs: 129 | desc: Generate Terraform documentation 130 | vars: 131 | ITEMS: 132 | sh: "task tf:get:modules" 133 | cmds: 134 | - for: { var: ITEMS } 135 | cmd: terraform-docs -c ./.terraform-docs.yml "{{osClean .ITEM}}" 136 | # - for: { var: ITEMS } 137 | # cmd: | 138 | # {{.PWSH}} '$name = Split-Path "{{osClean .ITEM}}" -Leaf; (Get-Content "./{{osClean .ITEM}}/README.md") -replace "__NAME__", $name | Set-Content "./{{osClean .ITEM}}/README.md"' 139 | # platforms: [windows] 140 | # - for: { var: ITEMS } 141 | # cmd: | 142 | # sed -i "s/__NAME__/$(basename "{{osClean .ITEM}}")/g" "./{{osClean .ITEM}}/README.md" 143 | # sed -i -e '$a\' "./{{osClean .ITEM}}/README.md" 144 | # platforms: [linux, darwin] 145 | dir: "{{.ROOT_DIR}}" 146 | 147 | # * Clean 148 | clean: 149 | desc: Clean up Terraform files 150 | cmds: 151 | - cmd: | 152 | find ./ -name ".external_modules" -type d -exec rm -rf {} \; 153 | find ./ -name ".terraform" -type d -exec rm -rf {} \; 154 | find ./ -name "*.terraform.lock.*" -type f -exec rm -f {} \; 155 | find ./ -name "*.tfstate*" -type f -exec rm -f {} \; 156 | platforms: [linux, darwin] 157 | - cmd: | 158 | {{.PWSH}} 'Get-ChildItem -Path ./ -Include ".external_modules" -Directory -Recurse | Remove-Item -Recurse -Force -Confirm:$false' 159 | {{.PWSH}} 'Get-ChildItem -Path ./ -Include ".terraform" -Directory -Recurse | Remove-Item -Recurse -Force -Confirm:$false' 160 | {{.PWSH}} 'Get-ChildItem -Path ./ -Include "*.terraform.lock.*" -File -Recurse | Remove-Item -Force -Confirm:$false' 161 | {{.PWSH}} 'Get-ChildItem -Path ./ -Include "*.tfstate*" -File -Recurse | Remove-Item -Force -Confirm:$false' 162 | platforms: [windows] 163 | dir: "{{.ROOT_DIR}}" 164 | 165 | test:unit:all: 166 | desc: Run terraform unit tests on all modules 167 | vars: 168 | ITEMS: 169 | sh: "task tf:get:modules" 170 | cmds: 171 | - for: { var: ITEMS } 172 | cmd: | 173 | pushd "{{osClean .ITEM}}" 174 | task tf:test:unit 175 | popd 176 | 177 | test:unit: 178 | desc: Run terraform unit tests 179 | sources: 180 | - "**/*unit*.tftest.hcl" 181 | preconditions: 182 | - sh: | 183 | {{if eq OS "windows"}} 184 | {{.PWSH}} 'if ((Get-ChildItem -Path . -Filter "*unit*.tftest.hcl" -Recurse).Count -eq 0) { exit 1 } else { exit 0 }' 185 | {{else}} 186 | [[ $(find . -name "*unit*.tftest.hcl" | wc -l) -gt 0 ]] 187 | {{end}} 188 | msg: "No unit tests found. Please create at least one unit test file named with pattern *unit*.tftest.hcl" 189 | cmds: 190 | - terraform init -upgrade -backend=false 191 | - for: sources 192 | cmd: terraform test -filter "{{osClean .ITEM}}" -junit-xml=testunit-results.xml 193 | dir: "{{.USER_WORKING_DIR}}" 194 | 195 | test:acc:all: 196 | desc: Run terraform unit tests on all modules 197 | vars: 198 | ITEMS: 199 | sh: "task tf:get:modules" 200 | cmds: 201 | - for: { var: ITEMS } 202 | cmd: | 203 | pushd "{{osClean .ITEM}}" 204 | task tf:test:acc 205 | popd 206 | 207 | test:acc: 208 | desc: Run terraform acceptance tests 209 | sources: 210 | - "**/*acc*.tftest.hcl" 211 | preconditions: 212 | - sh: | 213 | {{if eq OS "windows"}} 214 | {{.PWSH}} 'if ((Get-ChildItem -Path . -Filter "*acc*.tftest.hcl" -Recurse).Count -eq 0) { exit 1 } else { exit 0 }' 215 | {{else}} 216 | [[ $(find . -name "*acc*.tftest.hcl" | wc -l) -gt 0 ]] 217 | {{end}} 218 | msg: "No acceptance tests found. Please create at least one acceptance test file named with pattern *acc*.tftest.hcl" 219 | cmds: 220 | - terraform init -upgrade -backend=false 221 | - for: sources 222 | cmd: terraform test -filter "{{osClean .ITEM}}" -junit-xml=testacc-results.xml 223 | dir: "{{.USER_WORKING_DIR}}" 224 | 225 | get:modules: 226 | desc: Get all TF modules 227 | silent: true 228 | cmds: 229 | - cmd: find . -mindepth 2 -not -path "*/.terraform/*" -not -path "*/.external_modules/*" -type f -name "*.tf" -exec dirname {} \; | sort -u 230 | platforms: [linux, darwin] 231 | - cmd: | 232 | {{.PWSH}} '(Get-ChildItem . *.tf -Recurse).DirectoryName | Where-Object {$_ -notmatch "\\(.terraform|.external_modules)\\" } | Select-Object -Unique | ForEach-Object { Resolve-Path -Relative $_ }' 233 | platforms: [windows] 234 | dir: "{{.ROOT_DIR}}" 235 | -------------------------------------------------------------------------------- /.taskfiles/yaml.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | --- 4 | version: "3" 5 | 6 | includes: 7 | internal: internal.yml 8 | 9 | tasks: 10 | # * Install YAMLlint 11 | install:yamllint: 12 | desc: Install YAMLlint 13 | cmds: 14 | - task: internal:install:pipx 15 | vars: 16 | APP: yamllint 17 | 18 | # * Tools 19 | tools: 20 | desc: Install YAML tools 21 | cmds: 22 | - task: install:yamllint 23 | 24 | # * Lint 25 | lint: 26 | desc: Lint YAML files 27 | cmds: 28 | - yamllint -c ./.yamllint.yml . 29 | dir: "{{.ROOT_DIR}}" 30 | -------------------------------------------------------------------------------- /.terraform-docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ### To generate the output file to partially incorporate in the README.md, 3 | ### Execute this command in the Terraform module's code folder: 4 | # terraform-docs -c .terraform-docs.yml . 5 | 6 | formatter: "markdown table" # this is required 7 | 8 | header-from: _header.md 9 | footer-from: _footer.md 10 | 11 | recursive: 12 | enabled: false 13 | path: quickstarts 14 | include-main: true 15 | 16 | sections: 17 | hide: [] 18 | show: [] 19 | 20 | content: |- 21 | {{ .Header }} 22 | --- 23 | {{ .Requirements }} 24 | {{ .Providers }} 25 | {{ .Modules }} 26 | {{ .Resources }} 27 | {{ .Inputs }} 28 | {{ .Outputs }} 29 | {{ .Footer }} 30 | 31 | ## Limitations and Considerations 32 | 33 | - This example is provided as a sample only and is not intended for production use without further customization. 34 | output: 35 | file: README.md 36 | mode: replace 37 | template: |- 38 | 39 | {{ .Content }} 40 | 41 | 42 | output-values: 43 | enabled: false 44 | from: "" 45 | 46 | sort: 47 | enabled: true 48 | by: required 49 | 50 | settings: 51 | anchor: false 52 | color: true 53 | default: true 54 | description: false 55 | escape: true 56 | hide-empty: false 57 | html: true 58 | indent: 2 59 | lockfile: true 60 | read-comments: true 61 | required: true 62 | sensitive: true 63 | type: true 64 | -------------------------------------------------------------------------------- /.tflint.hcl: -------------------------------------------------------------------------------- 1 | # https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/config.md 2 | 3 | config { 4 | call_module_type = "local" 5 | force = false 6 | } 7 | 8 | plugin "terraform" { 9 | enabled = true 10 | preset = "recommended" 11 | } 12 | 13 | plugin "terraform" { 14 | enabled = true 15 | version = "0.12.0" 16 | source = "github.com/terraform-linters/tflint-ruleset-terraform" 17 | } 18 | 19 | plugin "basic-ext" { 20 | enabled = true 21 | version = "0.7.2" 22 | source = "github.com/Azure/tflint-ruleset-basic-ext" 23 | } 24 | 25 | plugin "azurerm" { 26 | enabled = true 27 | version = "0.28.0" 28 | source = "github.com/terraform-linters/tflint-ruleset-azurerm" 29 | } 30 | 31 | plugin "azurerm-ext" { 32 | enabled = true 33 | version = "0.6.1" 34 | source = "github.com/Azure/tflint-ruleset-azurerm-ext" 35 | } 36 | -------------------------------------------------------------------------------- /.trivy.yml: -------------------------------------------------------------------------------- 1 | # https://trivy.dev/latest/docs/references/configuration/config-file/ 2 | --- 3 | scan: 4 | scanners: 5 | - vuln 6 | - misconfig 7 | - secret 8 | - license 9 | misconfiguration: 10 | render-cause: 11 | - terraform 12 | scanners: 13 | - azure-arm 14 | - dockerfile 15 | - terraform 16 | - terraformplan-json 17 | - terraformplan-snapshot 18 | ignorefile: .trivyignore.yml 19 | -------------------------------------------------------------------------------- /.trivyignore.yml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "hashicorp.terraform", 4 | "hashicorp.hcl", 5 | "redhat.vscode-yaml", 6 | "golang.go", 7 | "DavidAnson.vscode-markdownlint", 8 | "task.vscode-task", 9 | "sanjulaganepola.github-local-actions", 10 | "takumii.markdowntable" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": { 3 | // Terraform MCP Server: https://github.com/hashicorp/terraform-mcp-server 4 | "terraform": { 5 | "command": "docker", 6 | "args": [ 7 | "run", 8 | "-i", 9 | "--rm", 10 | "hashicorp/terraform-mcp-server" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // files 3 | "files.trimTrailingWhitespace": true, 4 | "files.insertFinalNewline": true, 5 | "files.associations": { 6 | "**/.github/workflows/*.y*ml": "yaml", 7 | "CODEOWNERS": "ignore" 8 | }, 9 | // editor 10 | "editor.inlineSuggest.enabled": true, 11 | "editor.detectIndentation": true, 12 | "editor.formatOnSave": true, 13 | "editor.bracketPairColorization.enabled": true, 14 | // git 15 | "git.autorefresh": true, 16 | "git.autofetch": true, 17 | "git.fetchOnPull": true, 18 | "git.pruneOnFetch": true, 19 | "git.pullBeforeCheckout": true, 20 | "git.branchProtection": [ 21 | "main" 22 | ], 23 | "git.mergeEditor": true, 24 | "git.enableSmartCommit": true, 25 | // github 26 | "github.branchProtection": true, 27 | "githubPullRequests.showPullRequestNumberInTree": true, 28 | // yaml 29 | "[yaml]": { 30 | "editor.defaultFormatter": "redhat.vscode-yaml" 31 | }, 32 | "yaml.format.singleQuote": false, 33 | "yaml.schemaStore.enable": true, 34 | // markdown 35 | "[markdown]": { 36 | "editor.defaultFormatter": "DavidAnson.vscode-markdownlint", 37 | "editor.formatOnSave": true, 38 | "editor.formatOnPaste": true 39 | }, 40 | "markdownlint.config": { 41 | "extends": "./.markdownlint.yml" 42 | }, 43 | "markdowntable.paddedDelimiterRowPipes": false, 44 | // go 45 | "go.useLanguageServer": true, 46 | "go.toolsManagement.autoUpdate": true, 47 | "go.vetOnSave": "package", 48 | "[go]": { 49 | "editor.formatOnSave": true, 50 | "editor.defaultFormatter": "golang.go", 51 | "editor.codeActionsOnSave": { 52 | "source.organizeImports": "always" 53 | } 54 | }, 55 | "[go.mod]": { 56 | "editor.formatOnSave": true, 57 | "editor.codeActionsOnSave": { 58 | "source.organizeImports": "always" 59 | } 60 | }, 61 | "gopls": { 62 | "formatting.gofumpt": true, 63 | "ui.semanticTokens": true, 64 | "ui.completion.usePlaceholders": true 65 | }, 66 | "go.formatTool": "goimports", 67 | "go.diagnostic.vulncheck": "Imports", 68 | "go.testTimeout": "3m", 69 | "linter.linters": { 70 | "yamllint": { 71 | "configFiles": [ 72 | ".yamllint.yml" 73 | ] 74 | }, 75 | "markdownlint": { 76 | "configFiles": [ 77 | ".markdownlint.yml" 78 | ] 79 | }, 80 | "shellcheck": { 81 | "configFiles": [ 82 | ".shellcheckrc" 83 | ], 84 | "command": [ 85 | "shellcheck", 86 | "--format", 87 | "json", 88 | "--enable", 89 | "all", 90 | "--rcfile", 91 | "$config", 92 | "-" 93 | ] 94 | } 95 | }, 96 | "shellcheck.customArgs": [ 97 | "--rcfile", 98 | ".shellcheckrc" 99 | ], 100 | "shellcheck.useWorkspaceRootAsCwd": true, 101 | "sarif-viewer.connectToGithubCodeScanning": "on", 102 | "[terraform]": { 103 | "editor.defaultFormatter": "hashicorp.terraform" 104 | }, 105 | "[terraform-test]": { 106 | "editor.defaultFormatter": "hashicorp.terraform" 107 | }, 108 | "[hcl]": { 109 | "editor.defaultFormatter": "hashicorp.hcl" 110 | }, 111 | // copilot 112 | "chat.mcp.discovery.enabled": true, 113 | "github.copilot.chat.commitMessageGeneration.instructions": [ 114 | { 115 | "text": "Use the Conventional Commits specification to create commit message with scope to keep consistent commit history. Do not use file extension. Must be less than 100 characters." 116 | } 117 | ] 118 | } 119 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/yamllint.json 2 | # docs: https://yamllint.readthedocs.io/en/stable/configuration.html#extending-the-default-configuration 3 | --- 4 | extends: relaxed 5 | 6 | locale: en_US.UTF-8 7 | rules: 8 | document-start: 9 | level: warning 10 | line-length: disable 11 | -------------------------------------------------------------------------------- /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 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 👥 Contributing Guide 2 | 3 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 4 | 5 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 6 | 7 | ## 🔄️ Contribution process 8 | 9 | To contribute, please follow these steps: 10 | 11 | 1. Fork the project repository on GitHub. 12 | 1. Create a new branch for your feature or bug fix. 13 | 1. Make your changes and commit them with descriptive commit messages; check [Conventional Commits](https://www.conventionalcommits.org) as a suggestion. 14 | 1. Push to your forked repository or branch. 15 | 1. Create a new Pull Request from your fork to this project. 16 | 1. Please ensure that your pull request includes a detailed description of your changes and that your code adheres to the code style guidelines outlined below. 17 | 18 | ## ✍️ Types of Contributions 19 | 20 | We welcome feedback and suggestions only via GitHub Issues. Pull Request (PR) contributions will **NOT** be accepted at this time - it may change in the future. 21 | 22 | ## 🔰 Code of Conduct 23 | 24 | All contributors are expected to adhere to the project name code of conduct. Therefore, please review it before contributing [`Code of Conduct`](./CODE_OF_CONDUCT.md). 25 | 26 | ## 📄 License 27 | 28 | By contributing to this project, you agree that your contributions will be licensed under the project license. 29 | 30 | --- 31 | 32 | Thank you for contributing! 33 | -------------------------------------------------------------------------------- /DEVELOPER.md: -------------------------------------------------------------------------------- 1 | # Developer Guide 2 | 3 | - [Development Environment](#development-environment) 4 | - [Dev Container development](#dev-container-development) 5 | - [Prerequisites](#prerequisites) 6 | - [Opening the Dev Container](#opening-the-dev-container) 7 | - [Local development](#local-development) 8 | - [Requirements](#requirements) 9 | 10 | ## Development Environment 11 | 12 | Depends on your preferences you can use pre-configured development environment with Dev Container or your machine directly. 13 | 14 | ### Dev Container development 15 | 16 | Dev Container is the **preferred** way for contribution to the project. 17 | 18 | The [Dev Container](https://containers.dev/) feature in Visual Studio Code creates a consistent and isolated development environment. A Dev Container is a Docker container that has all the tools and dependencies needed to work with the codebase. You can open any folder inside the container and use VS Code's full feature set, including IntelliSense, code navigation, debugging, and extensions. 19 | 20 | #### Prerequisites 21 | 22 | To use the Dev Container in this repo, you need to have the following prerequisites: 23 | 24 | - [Docker Desktop](https://www.docker.com/products/docker-desktop/) 25 | - [Visual Studio Code](https://code.visualstudio.com/) 26 | - [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) installed in VS Code. 27 | 28 | #### Opening the Dev Container 29 | 30 | Once you have the prerequisites, you can follow these steps to open the repo in a Dev Container: 31 | 32 | 1. Clone or fork this repo to your local machine. 33 | 1. Open VS Code and press F1 to open the command palette. Type `Remote-Containers: Open Folder in Container...` and select it. 34 | 1. Browse to the folder where you cloned or forked the repo and click "Open". 35 | 1. VS Code will reload and start building the Dev Container image. This may take a few minutes depending on your network speed and the size of the image. 36 | 1. When the Dev Container is ready, you will see "Dev Container: Fabric Terraform Quickstarts" in the lower left corner of the VS Code status bar. You can also open a new terminal (Ctrl+Shift+`) and see that you are inside the container. 37 | 1. You can now edit, run, debug, and test the code as if you were on your local machine. Any changes you make will be reflected in the container and in your local file system. 38 | 39 | > [!NOTE] 40 | > To work with the repository you will need to verify or configure your GIT credentials, you can do it as follows in the dev Container terminal: 41 | 42 | - Verify Git user name and email: 43 | 44 | ```shell 45 | git config --list 46 | ``` 47 | 48 | You should see your username and email listed, if they do not appear or you want to change them you must establish them following the step below, (to quit the "git config" mode type "q"). 49 | 50 | - Change or set your Git username and email in the Dev Container: 51 | 52 | ```shell 53 | git config --global user.name "Your Name" 54 | git config --global user.email "your.email@address" 55 | ``` 56 | 57 | > [!NOTE] 58 | > If you logging to docker container's shell outside the VS Code, in order to work with git repository, run the following commands: 59 | 60 | ```shell 61 | export SSH_AUTH_SOCK=$(ls -t /tmp/vscode-ssh-auth* | head -1) 62 | export REMOTE_CONTAINERS_IPC=$(ls -t /tmp/vscode-remote-containers-ipc* | head -1) 63 | ``` 64 | 65 | For more information about Dev Containers, you can check out the [Dev Container documentation](https://code.visualstudio.com/docs/devcontainers/containers) and [sharing Git credentials with your container](https://code.visualstudio.com/remote/advancedcontainers/sharing-git-credentials). 66 | 67 | ### Local development 68 | 69 | Dev Container is the **preferred** way for contribution to the project. It contains all necessary tools and configuration that is ready to start corking on the code without thinking on development environment setup. 70 | 71 | Local development is still possible on Windows, Linux and macOS, but requires additional step to setup development environment. 72 | 73 | > [!NOTE] 74 | > Treat all instructions, commands or scripts in `Local development` section as examples. Depending on your local environment and configuration, the final commands or script may vary. 75 | 76 | #### Requirements 77 | 78 | | Tool | Version | Purpose | Notes | 79 | |----------------------------------------------------------------------------------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| 80 | | [Git](https://git-scm.com/downloads) | `>= 2.49.0` | Version Control System | | 81 | | [Go](https://go.dev/doc/install) | `>= 1.24.3` | GoLang based tests | We recommend you to use Go version manager [go-nv/goenv](https://github.com/go-nv/goenv/blob/master/INSTALL.md) `goenv install 1.24.3` | 82 | | [Python](https://www.python.org/) | `>= 3.13` | Runtime for Python-based tools | | 83 | | [NodeJS](https://nodejs.org/) | `>= 22` | Runtime for Node-based tools | | 84 | | [PIPX](https://pipx.pypa.io/) | `>= 1.7.1` | Running Python tools in isolated environments. | | 85 | | [Task](https://taskfile.dev/installation) | `>= 3.43.3` | Task runner | | 86 | | [Terraform](https://developer.hashicorp.com/terraform/downloads) | `>= 1.12.0` | Purpose | We recommend you to use Terraform version manager [tfutils/tfenv](https://github.com/tfutils/tfenv/blob/master/README.md). `tfenv install 1.12.0`, `tfenv use 1.12.0` | 87 | | [TFlint](https://github.com/terraform-linters/tflint) | `>= 0.57.0` | Terraform linter that checks Terraform configurations for errors, potential bugs, and stylistic or best-practice violations. | | 88 | | [Actionlint](https://github.com/rhysd/actionlint) | `>= 1.7.7` | GitHub Workflows linter | | 89 | | [act](https://github.com/nektos/act) | `>= 0.2.77` | Purpose | | 90 | | [GitHub CLI](https://cli.github.com/) | `>= 2.72.0` | Purpose | | 91 | | [golangci-lint](https://golangci-lint.run/) | `>= 2.1.6` | GoLang code linter | | 92 | | [govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck) | `>= 1.1.4` | Static application security testing (SAST) tool for GoLang | | 93 | | [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) | `>= 0.18.1` | Markdown linter | | 94 | | [markdown-table-formatter](https://github.com/nvuillam/markdown-table-formatter) | `>= 1.6.1` | Markdown table formatter | | 95 | | [Gitleaks](https://gitleaks.io/) | `>= 8.26.0` | Static application security testing (SAST) tool designed to detect and prevent secrets from being committed to Git repositories1. | | 96 | | [Trivy](https://trivy.dev/) | `>= 0.62` | Static application security testing (SAST) tool designed to find vulnerabilities (CVE) and misconfigurations (IaC) across code, artifacts, container images, and more. | | 97 | | [Checkov](https://www.checkov.io/) | `>= 3.2.424` | Static application security testing (SAST) tool designed to find vulnerabilities (CVE) and misconfigurations (IaC) across code, artifacts, and more. | | 98 | | [CSpell](https://cspell.org/) | `>= 9.0.1` | Spell checker for documentation | | 99 | | [CodeSpell](https://github.com/codespell-project/codespell) | `>= 2.4.1` | Spell checker for source code | | 100 | | [Lychee](https://lychee.cli.rs/) | `>= 0.18.1` | Link checker | | 101 | | [YAMLlint](https://yamllint.readthedocs.io/) | `>= 1.37.1` | YAML linter | | 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fabric Terraform Quick-Starts 2 | 3 | This repository contains example terraform modules that can be used to quickly deploy Fabric resources. The modules are intended to demonstrate some common scenarios where managing Fabric resources along side Azure, [Entra](https://entra.microsoft.com), or other resources can be facilitated with the [Terraform Provider for Microsoft Fabric](https://aka.ms/FabricTF). The modules are examples and are not intended to be used in production environments without modification. 4 | 5 | ## Contributing 6 | 7 | This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit . 8 | 9 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 12 | 13 | ## Trademarks 14 | 15 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. 16 | -------------------------------------------------------------------------------- /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) and [Xamarin](https://github.com/xamarin). 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/security.md/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/security.md/msrc/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/security.md/msrc/pgp). 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://www.microsoft.com/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/security.md/msrc/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/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | 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. 6 | 7 | ## Microsoft Support Policy 8 | 9 | Support for this **PROJECT** is limited to the resources listed above. 10 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://taskfile.dev/schema.json 2 | # docs: https://taskfile.dev 3 | # 4 | # --- Windows --- 5 | # winget install Task.Task 6 | # 7 | # --- Linux --- 8 | # sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin 9 | # echo 'command -v task >/dev/null || export PATH="$PATH:$HOME/.local/bin"' >> ~/.profile 10 | # source ~/.profile 11 | # 12 | # --- macOS --- 13 | # brew install go-task/tap/go-task 14 | --- 15 | version: "3" 16 | 17 | includes: 18 | internal: 19 | taskfile: .taskfiles/internal.yml 20 | gh: 21 | taskfile: .taskfiles/github.yml 22 | go: 23 | taskfile: .taskfiles/golang.yml 24 | md: 25 | taskfile: .taskfiles/markdown.yml 26 | sec: 27 | taskfile: .taskfiles/security.yml 28 | spell: 29 | taskfile: .taskfiles/spell.yml 30 | tf: 31 | taskfile: .taskfiles/terraform.yml 32 | yml: 33 | taskfile: .taskfiles/yaml.yml 34 | 35 | vars: 36 | REPO_ABS_ROOT_PATH: 37 | sh: git rev-parse --show-toplevel 38 | 39 | tasks: 40 | default: 41 | desc: Default task 42 | cmds: 43 | - task --list 44 | 45 | tools: 46 | desc: Install tools 47 | cmds: 48 | - task: gh:tools 49 | - task: go:tools 50 | - task: md:tools 51 | - task: sec:tools 52 | - task: spell:tools 53 | - task: tf:tools 54 | - task: yml:tools 55 | 56 | lint: 57 | desc: Lint, format, check files 58 | cmds: 59 | - task: gh:lint 60 | - task: go:lint 61 | - task: sec:lint 62 | - task: tf:lint 63 | - task: yml:lint 64 | - task: md:lint 65 | - task: spell:lint 66 | 67 | docs: 68 | desc: Generate documentation 69 | cmds: 70 | - task: tf:docs 71 | - task: spell:lint 72 | - task: md:lint 73 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Hello Fabric (100 level) 3 | 4 | --- 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |-----------|---------------| 10 | | terraform | >= 1.8, < 2.0 | 11 | | fabric | 1.2.0 | 12 | 13 | ## Providers 14 | 15 | | Name | Version | 16 | |--------|---------| 17 | | fabric | 1.2.0 | 18 | 19 | ## Modules 20 | 21 | No modules. 22 | 23 | ## Resources 24 | 25 | | Name | Type | 26 | |---------------------------------------------------------------------------------------------------------------------|----------| 27 | | [fabric_workspace.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/workspace) | resource | 28 | 29 | ## Inputs 30 | 31 | | Name | Description | Type | Default | Required | 32 | |-----------------|-----------------------|----------|---------|:--------:| 33 | | workspace\_name | Name of the Workspace | `string` | n/a | yes | 34 | 35 | ## Outputs 36 | 37 | | Name | Description | 38 | |---------------|-----------------------------| 39 | | workspace | The Fabric workspace object | 40 | | workspace\_id | The Fabric workspace ID | 41 | 42 | ## Usage 43 | 44 | Execute example with the following commands: 45 | 46 | ```shell 47 | terraform init 48 | terraform apply 49 | ``` 50 | 51 | ## Limitations and Considerations 52 | 53 | - This example is provided as a sample only and is not intended for production use without further customization. 54 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/_footer.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Execute example with the following commands: 4 | 5 | ```shell 6 | terraform init 7 | terraform apply 8 | ``` 9 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/_header.md: -------------------------------------------------------------------------------- 1 | # Hello Fabric (100 level) 2 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/main.tf: -------------------------------------------------------------------------------- 1 | resource "fabric_workspace" "example" { 2 | display_name = var.workspace_name 3 | } 4 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/outputs.tf: -------------------------------------------------------------------------------- 1 | output "workspace" { 2 | value = resource.fabric_workspace.example 3 | description = "The Fabric workspace object" 4 | } 5 | 6 | output "workspace_id" { 7 | value = resource.fabric_workspace.example.id 8 | description = "The Fabric workspace ID" 9 | } 10 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/providers.tf: -------------------------------------------------------------------------------- 1 | # We strongly recommend using the required_providers block to set the Fabric Provider source and version being used 2 | terraform { 3 | required_version = ">= 1.8, < 2.0" 4 | required_providers { 5 | fabric = { 6 | source = "microsoft/fabric" 7 | version = "1.2.0" 8 | } 9 | } 10 | } 11 | 12 | # Configure the Microsoft Fabric Provider 13 | provider "fabric" {} 14 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/tests/test_acc.tftest.hcl: -------------------------------------------------------------------------------- 1 | run "random" { 2 | module { 3 | source = "../../tests/random_generator" 4 | } 5 | } 6 | 7 | provider "fabric" {} 8 | 9 | run "testacc_101-hello-fabric" { 10 | providers = { 11 | fabric = fabric 12 | } 13 | 14 | variables { 15 | workspace_name = "ws-${run.random.string}" 16 | } 17 | 18 | command = apply 19 | 20 | assert { 21 | condition = fabric_workspace.example.display_name == "ws-${run.random.string}" 22 | error_message = "workspace display_name was not assigned correctly" 23 | } 24 | 25 | assert { 26 | condition = length(fabric_workspace.example.id) != 0 27 | error_message = "workspace id was not assigned correctly" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/tests/test_unit.tftest.hcl: -------------------------------------------------------------------------------- 1 | mock_provider "fabric" { 2 | alias = "fake" 3 | source = "../../tests/mocks/fabric" 4 | } 5 | 6 | run "testunit_101-hello-fabric" { 7 | providers = { 8 | fabric = fabric.fake 9 | } 10 | 11 | variables { 12 | workspace_name = "ws-test" 13 | } 14 | 15 | command = apply 16 | 17 | assert { 18 | condition = fabric_workspace.example.display_name == "ws-test" 19 | error_message = "workspace display_name was not assigned correctly" 20 | } 21 | 22 | assert { 23 | condition = length(fabric_workspace.example.id) != 0 24 | error_message = "workspace id was not assigned correctly" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /quickstarts/101-hello-fabric/variables.tf: -------------------------------------------------------------------------------- 1 | variable "workspace_name" { 2 | description = "Name of the Workspace" 3 | type = string 4 | } 5 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Fabric Capacity (100 level) 3 | 4 | --- 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |-----------|---------------| 10 | | terraform | >= 1.8, < 2.0 | 11 | | azuread | 3.4.0 | 12 | | azurerm | 4.32.0 | 13 | | fabric | 1.2.0 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |---------|---------| 19 | | azuread | 3.4.0 | 20 | | azurerm | 4.32.0 | 21 | | fabric | 1.2.0 | 22 | 23 | ## Modules 24 | 25 | No modules. 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |----------------------------------------------------------------------------------------------------------------------------------------|-------------| 31 | | [azurerm_fabric_capacity.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/fabric_capacity) | resource | 32 | | [azurerm_resource_group.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/resource_group) | resource | 33 | | [fabric_workspace.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/workspace) | resource | 34 | | [azuread_directory_object.example](https://registry.terraform.io/providers/hashicorp/azuread/3.4.0/docs/data-sources/directory_object) | data source | 35 | | [azuread_user.example](https://registry.terraform.io/providers/hashicorp/azuread/3.4.0/docs/data-sources/user) | data source | 36 | | [azurerm_client_config.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/data-sources/client_config) | data source | 37 | | [fabric_capacity.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/data-sources/capacity) | data source | 38 | 39 | ## Inputs 40 | 41 | | Name | Description | Type | Default | Required | 42 | |-------------------------------|---------------------------------------------------|---------------|-------------|:--------:| 43 | | solution\_name | Name of the solution | `string` | n/a | yes | 44 | | subscription\_id | The Azure subscription ID | `string` | n/a | yes | 45 | | fabric\_capacity\_admin\_upns | Collection of admin UPNs for the Fabric Capacity. | `set(string)` | `[]` | no | 46 | | fabric\_capacity\_sku | Fabric Capacity SKU name | `string` | `"F2"` | no | 47 | | location | Location of the Azure resources | `string` | `"WestUS3"` | no | 48 | 49 | ## Outputs 50 | 51 | | Name | Description | 52 | |-------------------|-----------------------------| 53 | | fabric\_capacity | The Fabric Capacity object | 54 | | fabric\_workspace | The Fabric Workspace object | 55 | 56 | ## Usage 57 | 58 | Execute example with the following commands: 59 | 60 | ```shell 61 | terraform init 62 | terraform apply 63 | ``` 64 | 65 | ## Limitations and Considerations 66 | 67 | - This example is provided as a sample only and is not intended for production use without further customization. 68 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/_footer.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Execute example with the following commands: 4 | 5 | ```shell 6 | terraform init 7 | terraform apply 8 | ``` 9 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/_header.md: -------------------------------------------------------------------------------- 1 | # Fabric Capacity (100 level) 2 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/main.tf: -------------------------------------------------------------------------------- 1 | # Access the client configuration of the AzureRM provider. 2 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config 3 | data "azurerm_client_config" "example" {} 4 | 5 | # Check the directory object type. 6 | # https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/directory_object 7 | data "azuread_directory_object" "example" { 8 | object_id = data.azurerm_client_config.example.object_id 9 | } 10 | 11 | # Get information about Entra user. 12 | # https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/user 13 | data "azuread_user" "example" { 14 | object_id = data.azurerm_client_config.example.object_id 15 | } 16 | 17 | locals { 18 | # Check if the client is a user or not, if it is a user then use the user principal name otherwise use the object id for service principal. 19 | fabric_admin = can(data.azuread_directory_object.example.type == "User") ? data.azuread_user.example.user_principal_name : data.azurerm_client_config.example.object_id 20 | } 21 | 22 | # Create a resource group. 23 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group 24 | resource "azurerm_resource_group" "example" { 25 | name = "rg-${var.solution_name}" 26 | location = var.location 27 | } 28 | 29 | # Create a Fabric Capacity. 30 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/fabric_capacity 31 | resource "azurerm_fabric_capacity" "example" { 32 | name = "fc${var.solution_name}" 33 | resource_group_name = azurerm_resource_group.example.name 34 | location = var.location 35 | 36 | administration_members = setunion([local.fabric_admin], var.fabric_capacity_admin_upns) 37 | 38 | sku { 39 | name = var.fabric_capacity_sku 40 | tier = "Fabric" 41 | } 42 | } 43 | 44 | # Get the Fabric Capacity details. 45 | # https://registry.terraform.io/providers/microsoft/fabric/latest/docs/data-sources/capacity 46 | data "fabric_capacity" "example" { 47 | display_name = azurerm_fabric_capacity.example.name 48 | 49 | lifecycle { 50 | postcondition { 51 | condition = self.state == "Active" 52 | error_message = "Fabric Capacity is not in Active state. Please check the Fabric Capacity status." 53 | } 54 | } 55 | } 56 | 57 | # Create a Fabric Workspace. 58 | # https://registry.terraform.io/providers/microsoft/fabric/latest/docs/resources/workspace 59 | resource "fabric_workspace" "example" { 60 | capacity_id = data.fabric_capacity.example.id 61 | display_name = "ws-${var.solution_name}" 62 | } 63 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/outputs.tf: -------------------------------------------------------------------------------- 1 | # Details of the Fabric Capacity 2 | output "fabric_capacity" { 3 | value = data.fabric_capacity.example 4 | description = "The Fabric Capacity object" 5 | } 6 | 7 | # Details of the Fabric Workspace 8 | output "fabric_workspace" { 9 | value = fabric_workspace.example 10 | description = "The Fabric Workspace object" 11 | } 12 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.8, < 2.0" 3 | required_providers { 4 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = "4.32.0" 8 | } 9 | # https://registry.terraform.io/providers/hashicorp/azuread/latest 10 | azuread = { 11 | source = "hashicorp/azuread" 12 | version = "3.4.0" 13 | } 14 | # https://registry.terraform.io/providers/microsoft/fabric/latest 15 | fabric = { 16 | source = "microsoft/fabric" 17 | version = "1.2.0" 18 | } 19 | } 20 | } 21 | 22 | provider "azurerm" { 23 | features {} 24 | # Configuration options 25 | 26 | subscription_id = var.subscription_id 27 | } 28 | 29 | provider "azuread" { 30 | # Configuration options 31 | } 32 | 33 | provider "fabric" { 34 | # Configuration options 35 | } 36 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/tests/test_unit.tftest.hcl: -------------------------------------------------------------------------------- 1 | mock_provider "azurerm" { 2 | alias = "fake" 3 | source = "../../tests/mocks/azurerm" 4 | } 5 | 6 | mock_provider "azuread" { 7 | alias = "fake" 8 | source = "../../tests/mocks/azuread" 9 | } 10 | 11 | mock_provider "fabric" { 12 | alias = "fake" 13 | source = "../../tests/mocks/fabric" 14 | } 15 | 16 | run "testunit_102-fabric-capacity" { 17 | providers = { 18 | azurerm = azurerm.fake 19 | azuread = azuread.fake 20 | fabric = fabric.fake 21 | } 22 | 23 | variables { 24 | subscription_id = "00000000-0000-0000-0000-000000000000" 25 | solution_name = "test" 26 | location = "westus2" 27 | fabric_capacity_sku = "F4" 28 | fabric_capacity_admin_upns = ["example@example.onmicrosoft.com"] 29 | } 30 | 31 | command = apply 32 | 33 | # Fabric Workspace 34 | assert { 35 | condition = fabric_workspace.example.display_name == "ws-test" 36 | error_message = "workspace display_name was not assigned correctly" 37 | } 38 | 39 | assert { 40 | condition = fabric_workspace.example.capacity_id == data.fabric_capacity.example.id 41 | error_message = "workspace capacity_id was not assigned correctly" 42 | } 43 | 44 | assert { 45 | condition = length(fabric_workspace.example.id) != 0 46 | error_message = "workspace id was not assigned correctly" 47 | } 48 | 49 | # Fabric Capacity 50 | assert { 51 | condition = data.fabric_capacity.example.display_name == "fctest" 52 | error_message = "capacity display_name was not assigned correctly" 53 | } 54 | 55 | assert { 56 | condition = length(data.fabric_capacity.example.id) != 0 57 | error_message = "capacity id was not assigned correctly" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /quickstarts/102-fabric-capacity/variables.tf: -------------------------------------------------------------------------------- 1 | variable "subscription_id" { 2 | description = "The Azure subscription ID" 3 | type = string 4 | sensitive = false 5 | nullable = false 6 | } 7 | 8 | variable "solution_name" { 9 | description = "Name of the solution" 10 | type = string 11 | sensitive = false 12 | nullable = false 13 | } 14 | 15 | variable "location" { 16 | description = "Location of the Azure resources" 17 | type = string 18 | sensitive = false 19 | nullable = false 20 | default = "WestUS3" 21 | } 22 | 23 | variable "fabric_capacity_sku" { 24 | description = "Fabric Capacity SKU name" 25 | type = string 26 | sensitive = false 27 | nullable = false 28 | default = "F2" 29 | 30 | validation { 31 | condition = contains(["F2", "F4", "F8", "F16", "F32", "F64", "F128", "F256", "F512", "F1024", "F2048"], var.fabric_capacity_sku) 32 | error_message = "Please specify a valid Fabric Capacity SKU. Valid values are: [ 'F2', 'F4', 'F8', 'F16', 'F32', 'F64', 'F128', 'F256', 'F512', 'F1024', 'F2048' ]." 33 | } 34 | } 35 | 36 | variable "fabric_capacity_admin_upns" { 37 | description = "Collection of admin UPNs for the Fabric Capacity." 38 | type = set(string) 39 | sensitive = false 40 | nullable = false 41 | default = [] 42 | } 43 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Fabric Virtual Network Gateway (200 level) 3 | 4 | --- 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |-----------|---------------| 10 | | terraform | >= 1.8, < 2.0 | 11 | | azuread | 3.4.0 | 12 | | azurerm | 4.32.0 | 13 | | fabric | 1.2.0 | 14 | 15 | ## Providers 16 | 17 | | Name | Version | 18 | |---------|---------| 19 | | azuread | 3.4.0 | 20 | | azurerm | 4.32.0 | 21 | | fabric | 1.2.0 | 22 | 23 | ## Modules 24 | 25 | No modules. 26 | 27 | ## Resources 28 | 29 | | Name | Type | 30 | |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| 31 | | [azurerm_network_security_group.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/network_security_group) | resource | 32 | | [azurerm_resource_group.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/resource_group) | resource | 33 | | [azurerm_role_assignment.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/role_assignment) | resource | 34 | | [azurerm_subnet.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/subnet) | resource | 35 | | [azurerm_subnet_network_security_group_association.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/subnet_network_security_group_association) | resource | 36 | | [azurerm_virtual_network.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/resources/virtual_network) | resource | 37 | | [fabric_gateway.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/gateway) | resource | 38 | | [fabric_gateway_role_assignment.example_admin](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/gateway_role_assignment) | resource | 39 | | [fabric_gateway_role_assignment.example_connection_creator](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/gateway_role_assignment) | resource | 40 | | [azuread_group.example_admin](https://registry.terraform.io/providers/hashicorp/azuread/3.4.0/docs/data-sources/group) | data source | 41 | | [azuread_group.example_connection_creator](https://registry.terraform.io/providers/hashicorp/azuread/3.4.0/docs/data-sources/group) | data source | 42 | | [azurerm_client_config.example](https://registry.terraform.io/providers/hashicorp/azurerm/4.32.0/docs/data-sources/client_config) | data source | 43 | | [fabric_capacity.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/data-sources/capacity) | data source | 44 | 45 | ## Inputs 46 | 47 | | Name | Description | Type | Default | Required | 48 | |---------------------------------------|--------------------------------------------------------------|----------|-------------|:--------:| 49 | | fabric\_capacity\_name | Existing Fabric Capacity name | `string` | n/a | yes | 50 | | solution\_name | Name of the solution | `string` | n/a | yes | 51 | | subscription\_id | The Azure subscription ID | `string` | n/a | yes | 52 | | fabric\_vnet\_gw\_admin | Entra Group name for Fabric VNet Gateway admins | `string` | `null` | no | 53 | | fabric\_vnet\_gw\_connection\_creator | Entra Group name for Fabric VNet Gateway connection creators | `string` | `null` | no | 54 | | location | Location of the Azure resources | `string` | `"WestUS2"` | no | 55 | 56 | ## Outputs 57 | 58 | | Name | Description | 59 | |---------------------------|----------------------------------| 60 | | azurerm\_subnet | The Azure VNet Subnet object | 61 | | azurerm\_virtual\_network | The Azure Virtual Network object | 62 | | fabric\_gateway | The Fabric Gateway object | 63 | 64 | ## Usage 65 | 66 | Execute example with the following commands: 67 | 68 | ```shell 69 | terraform init 70 | terraform apply 71 | ``` 72 | 73 | ## Limitations and Considerations 74 | 75 | - This example is provided as a sample only and is not intended for production use without further customization. 76 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/_footer.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Execute example with the following commands: 4 | 5 | ```shell 6 | terraform init 7 | terraform apply 8 | ``` 9 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/_header.md: -------------------------------------------------------------------------------- 1 | # Fabric Virtual Network Gateway (200 level) 2 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/main.tf: -------------------------------------------------------------------------------- 1 | 2 | # Access the client configuration of the AzureRM provider. 3 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config 4 | data "azurerm_client_config" "example" {} 5 | 6 | # Create a resource group. 7 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group 8 | resource "azurerm_resource_group" "example" { 9 | name = "rg-${var.solution_name}" 10 | location = var.location 11 | } 12 | 13 | # Create Azure virtual network 14 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_network 15 | resource "azurerm_virtual_network" "example" { 16 | name = "vnet-${var.solution_name}" 17 | address_space = ["10.0.0.0/16"] 18 | location = azurerm_resource_group.example.location 19 | resource_group_name = azurerm_resource_group.example.name 20 | } 21 | 22 | # Assign Network Contributor role to the Virtual Network 23 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment 24 | resource "azurerm_role_assignment" "example" { 25 | scope = azurerm_virtual_network.example.id 26 | principal_id = data.azurerm_client_config.example.object_id 27 | role_definition_name = "Network Contributor" 28 | } 29 | 30 | # Create a subnet for the virtual network with service delegation 31 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet 32 | resource "azurerm_subnet" "example" { 33 | name = "snet-default" 34 | resource_group_name = azurerm_resource_group.example.name 35 | virtual_network_name = azurerm_virtual_network.example.name 36 | address_prefixes = ["10.0.1.0/24"] 37 | 38 | delegation { 39 | name = "delegation" 40 | 41 | service_delegation { 42 | name = "Microsoft.PowerPlatform/vnetaccesslinks" 43 | actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] 44 | } 45 | } 46 | } 47 | 48 | # Create a network security group 49 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group 50 | resource "azurerm_network_security_group" "example" { 51 | name = "nsg-default" 52 | location = azurerm_resource_group.example.location 53 | resource_group_name = azurerm_resource_group.example.name 54 | } 55 | 56 | # Assign network security group to the subnet 57 | resource "azurerm_subnet_network_security_group_association" "example" { 58 | subnet_id = azurerm_subnet.example.id 59 | network_security_group_id = azurerm_network_security_group.example.id 60 | } 61 | 62 | # Get the Fabric Capacity details. 63 | # https://registry.terraform.io/providers/microsoft/fabric/latest/docs/data-sources/capacity 64 | data "fabric_capacity" "example" { 65 | display_name = var.fabric_capacity_name 66 | 67 | lifecycle { 68 | postcondition { 69 | condition = self.state == "Active" 70 | error_message = "Fabric Capacity is not in Active state. Please check the Fabric Capacity status." 71 | } 72 | } 73 | } 74 | 75 | # Create a Fabric Gateway 76 | # https://registry.terraform.io/providers/microsoft/fabric/latest/docs/resources/gateway 77 | resource "fabric_gateway" "example" { 78 | type = "VirtualNetwork" 79 | display_name = "gw-${var.solution_name}" 80 | inactivity_minutes_before_sleep = 30 81 | number_of_member_gateways = 1 82 | virtual_network_azure_resource = { 83 | resource_group_name = azurerm_resource_group.example.name 84 | virtual_network_name = azurerm_virtual_network.example.name 85 | subnet_name = azurerm_subnet.example.name 86 | subscription_id = var.subscription_id 87 | } 88 | capacity_id = data.fabric_capacity.example.id 89 | 90 | depends_on = [ 91 | azurerm_subnet.example, 92 | azurerm_role_assignment.example 93 | ] 94 | } 95 | 96 | # Get the group details 97 | # https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group 98 | data "azuread_group" "example_admin" { 99 | count = var.fabric_vnet_gw_admin != null ? 1 : 0 100 | 101 | display_name = var.fabric_vnet_gw_admin 102 | } 103 | 104 | # Assign roles to the Fabric Gateway 105 | # https://registry.terraform.io/providers/microsoft/fabric/latest/docs/resources/gateway_role_assignment 106 | resource "fabric_gateway_role_assignment" "example_admin" { 107 | count = var.fabric_vnet_gw_admin != null ? 1 : 0 108 | 109 | gateway_id = fabric_gateway.example.id 110 | principal = { 111 | id = data.azuread_group.example_admin[count.index].object_id 112 | type = "Group" 113 | } 114 | role = "Admin" 115 | } 116 | 117 | # Get the group details 118 | # https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group 119 | data "azuread_group" "example_connection_creator" { 120 | count = var.fabric_vnet_gw_connection_creator != null ? 1 : 0 121 | 122 | display_name = var.fabric_vnet_gw_connection_creator 123 | } 124 | 125 | # Assign roles to the Fabric Gateway 126 | # https://registry.terraform.io/providers/microsoft/fabric/latest/docs/resources/gateway_role_assignment 127 | resource "fabric_gateway_role_assignment" "example_connection_creator" { 128 | count = var.fabric_vnet_gw_connection_creator != null ? 1 : 0 129 | 130 | gateway_id = fabric_gateway.example.id 131 | principal = { 132 | id = data.azuread_group.example_connection_creator[count.index].object_id 133 | type = "Group" 134 | } 135 | role = "ConnectionCreator" 136 | } 137 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/outputs.tf: -------------------------------------------------------------------------------- 1 | output "fabric_gateway" { 2 | value = fabric_gateway.example 3 | description = "The Fabric Gateway object" 4 | } 5 | 6 | output "azurerm_virtual_network" { 7 | value = azurerm_virtual_network.example 8 | description = "The Azure Virtual Network object" 9 | } 10 | 11 | output "azurerm_subnet" { 12 | value = azurerm_subnet.example 13 | description = "The Azure VNet Subnet object" 14 | } 15 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.8, < 2.0" 3 | required_providers { 4 | # https://registry.terraform.io/providers/hashicorp/azurerm/latest 5 | azurerm = { 6 | source = "hashicorp/azurerm" 7 | version = "4.32.0" 8 | } 9 | # https://registry.terraform.io/providers/hashicorp/azuread/latest 10 | azuread = { 11 | source = "hashicorp/azuread" 12 | version = "3.4.0" 13 | } 14 | # https://registry.terraform.io/providers/microsoft/fabric/latest 15 | fabric = { 16 | source = "microsoft/fabric" 17 | version = "1.2.0" 18 | } 19 | } 20 | } 21 | 22 | provider "azurerm" { 23 | features {} 24 | # Configuration options 25 | subscription_id = var.subscription_id 26 | } 27 | 28 | provider "fabric" { 29 | # Configuration options 30 | preview = true 31 | } 32 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/tests/test_unit.tftest.hcl: -------------------------------------------------------------------------------- 1 | mock_provider "azurerm" { 2 | alias = "fake" 3 | source = "../../tests/mocks/azurerm" 4 | 5 | mock_resource "azurerm_role_assignment" { 6 | defaults = { 7 | id = "subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Authorization/roleAssignments/00000000-0000-0000-0000-000000000000" 8 | principal_id = "00000000-0000-0000-0000-000000000000" 9 | role_definition_id = "00000000-0000-0000-0000-000000000000" 10 | role_definition_name = "Network Contributor" 11 | scope = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/virtualNetworks/vnet-test" 12 | } 13 | } 14 | } 15 | 16 | mock_provider "azuread" { 17 | alias = "fake" 18 | source = "../../tests/mocks/azuread" 19 | } 20 | 21 | mock_provider "fabric" { 22 | alias = "fake" 23 | source = "../../tests/mocks/fabric" 24 | } 25 | 26 | run "testunit_202-fabric-vnet-gateway" { 27 | providers = { 28 | azurerm = azurerm.fake 29 | azuread = azuread.fake 30 | fabric = fabric.fake 31 | } 32 | 33 | variables { 34 | subscription_id = "00000000-0000-0000-0000-000000000000" 35 | solution_name = "test" 36 | location = "westus2" 37 | fabric_capacity_name = "fctest" 38 | fabric_vnet_gw_admin = "sg-test" 39 | fabric_vnet_gw_connection_creator = "sg-test" 40 | } 41 | 42 | command = apply 43 | 44 | # Azure Virtual Network 45 | assert { 46 | condition = azurerm_virtual_network.example.name == "vnet-test" 47 | error_message = "virtual network display_name was not assigned correctly" 48 | } 49 | 50 | assert { 51 | condition = length(azurerm_virtual_network.example.id) != 0 52 | error_message = "virtual network id was not assigned correctly" 53 | } 54 | 55 | # Fabric Gateway 56 | assert { 57 | condition = fabric_gateway.example.display_name == "gw-test" 58 | error_message = "gateway display_name was not assigned correctly" 59 | } 60 | 61 | assert { 62 | condition = length(fabric_gateway.example.id) != 0 63 | error_message = "gateway id was not assigned correctly" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /quickstarts/202-fabric-vnet-gateway/variables.tf: -------------------------------------------------------------------------------- 1 | variable "subscription_id" { 2 | description = "The Azure subscription ID" 3 | type = string 4 | sensitive = false 5 | nullable = false 6 | } 7 | 8 | variable "solution_name" { 9 | description = "Name of the solution" 10 | type = string 11 | sensitive = false 12 | nullable = false 13 | } 14 | 15 | variable "location" { 16 | description = "Location of the Azure resources" 17 | type = string 18 | sensitive = false 19 | nullable = false 20 | default = "WestUS2" 21 | } 22 | 23 | variable "fabric_capacity_name" { 24 | description = "Existing Fabric Capacity name" 25 | type = string 26 | sensitive = false 27 | nullable = false 28 | } 29 | 30 | variable "fabric_vnet_gw_admin" { 31 | description = "Entra Group name for Fabric VNet Gateway admins" 32 | type = string 33 | sensitive = false 34 | default = null 35 | } 36 | 37 | variable "fabric_vnet_gw_connection_creator" { 38 | description = "Entra Group name for Fabric VNet Gateway connection creators" 39 | type = string 40 | sensitive = false 41 | default = null 42 | } 43 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Testing with Terraform (300 level) 3 | 4 | Automated tests can be written to verify Fabric resources deployment using the Fabric Terraform Provider. 5 | 6 | Checkout how to write automated unit tests against this module using [Terraform](https://developer.hashicorp.com/terraform/cli/test) in the sample tests: 7 | 8 | - [tests/test\_unit.tftest.hcl](./tests/test\_unit.tftest.hcl) 9 | - [tests/test\_input.tftest.hcl](./tests/test\_input.tftest.hcl) 10 | 11 | --- 12 | 13 | ## Requirements 14 | 15 | | Name | Version | 16 | |-----------|---------------| 17 | | terraform | >= 1.8, < 2.0 | 18 | | fabric | 1.2.0 | 19 | 20 | ## Providers 21 | 22 | | Name | Version | 23 | |--------|---------| 24 | | fabric | 1.2.0 | 25 | 26 | ## Modules 27 | 28 | No modules. 29 | 30 | ## Resources 31 | 32 | | Name | Type | 33 | |-----------------------------------------------------------------------------------------------------------------------------------------------------|----------| 34 | | [fabric_workspace.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/workspace) | resource | 35 | | [fabric_workspace_role_assignment.example](https://registry.terraform.io/providers/microsoft/fabric/1.2.0/docs/resources/workspace_role_assignment) | resource | 36 | 37 | ## Inputs 38 | 39 | | Name | Description | Type | Default | Required | 40 | |------------------------|----------------------------------------|--------------------------------------------------------------------------|----------------------|:--------:| 41 | | principal | Workspace role assignment Principal ID |
object({
id = string
type = string
})
| n/a | yes | 42 | | workspace\_description | Description of the Workspace | `string` | `"test_description"` | no | 43 | | workspace\_name | Name of the Workspace | `string` | `"test_workspace"` | no | 44 | 45 | ## Outputs 46 | 47 | | Name | Description | 48 | |---------------------------------|-----------------------------------------| 49 | | workspace\_id | The Fabric workspace ID | 50 | | workspace\_role\_assignment\_id | The Fabric workspace role assignment ID | 51 | 52 | ## Usage 53 | 54 | Execute example with the following commands: 55 | 56 | ```shell 57 | terraform init 58 | terraform apply 59 | ``` 60 | 61 | ## Testing 62 | 63 | Running Terraform tests: 64 | 65 | ```shell 66 | # Run all tests 67 | terraform test 68 | 69 | # Run selected test 70 | terraform test -filter tests/test_unit.tftest.hcl 71 | ``` 72 | 73 | ## Limitations and Considerations 74 | 75 | - This example is provided as a sample only and is not intended for production use without further customization. 76 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/_footer.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Execute example with the following commands: 4 | 5 | ```shell 6 | terraform init 7 | terraform apply 8 | ``` 9 | 10 | ## Testing 11 | 12 | Running Terraform tests: 13 | 14 | ```shell 15 | # Run all tests 16 | terraform test 17 | 18 | # Run selected test 19 | terraform test -filter tests/test_unit.tftest.hcl 20 | ``` 21 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/_header.md: -------------------------------------------------------------------------------- 1 | # Testing with Terraform (300 level) 2 | 3 | Automated tests can be written to verify Fabric resources deployment using the Fabric Terraform Provider. 4 | 5 | Checkout how to write automated unit tests against this module using [Terraform](https://developer.hashicorp.com/terraform/cli/test) in the sample tests: 6 | 7 | - [tests/test_unit.tftest.hcl](./tests/test_unit.tftest.hcl) 8 | - [tests/test_input.tftest.hcl](./tests/test_input.tftest.hcl) 9 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "fabric_workspace" "example" { 2 | display_name = var.workspace_name 3 | description = var.workspace_description 4 | } 5 | 6 | resource "fabric_workspace_role_assignment" "example" { 7 | workspace_id = fabric_workspace.example.id 8 | principal = var.principal 9 | role = "Admin" 10 | } 11 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "workspace_id" { 2 | value = fabric_workspace.example.id 3 | description = "The Fabric workspace ID" 4 | } 5 | 6 | output "workspace_role_assignment_id" { 7 | value = fabric_workspace_role_assignment.example.id 8 | description = "The Fabric workspace role assignment ID" 9 | } 10 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.8, < 2.0" 3 | required_providers { 4 | fabric = { 5 | source = "microsoft/fabric" 6 | version = "1.2.0" 7 | } 8 | } 9 | } 10 | 11 | provider "fabric" {} 12 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/tests/test_input.tftest.hcl: -------------------------------------------------------------------------------- 1 | run "valid_workspace_display_name" { 2 | variables { 3 | workspace_name = "test_workspace" 4 | principal = { 5 | id = "00000000-0000-0000-0000-000000000000" 6 | type = "User" 7 | } 8 | } 9 | 10 | command = plan 11 | 12 | assert { 13 | condition = fabric_workspace.example.display_name == "test_workspace" 14 | error_message = "workspace display name did not match expected" 15 | } 16 | 17 | } 18 | 19 | run "invalid_workspace_role_assignment_principal" { 20 | variables { 21 | principal = { 22 | id = "test" 23 | type = "User" 24 | } 25 | } 26 | 27 | command = plan 28 | 29 | expect_failures = [var.principal.id] 30 | } 31 | 32 | run "valid_default_workspace_role_assignment_principal" { 33 | variables { 34 | principal = { 35 | id = "00000000-0000-0000-0000-000000000000" 36 | type = "ServicePrincipal" 37 | } 38 | } 39 | 40 | command = plan 41 | 42 | assert { 43 | condition = fabric_workspace_role_assignment.example.principal.id == "00000000-0000-0000-0000-000000000000" 44 | error_message = "invalid workspace role assignment principal_id" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/tests/test_unit.tftest.hcl: -------------------------------------------------------------------------------- 1 | mock_provider "fabric" { 2 | alias = "fake" 3 | 4 | mock_resource "fabric_workspace" { 5 | defaults = { 6 | id = "00000000-0000-0000-0000-000000000000" 7 | display_name = "ws-test" 8 | capacity_id = "00000000-0000-0000-0000-000000000000" 9 | capacity_region = "westus2" 10 | description = "ws-test-description" 11 | identity = null 12 | } 13 | } 14 | 15 | mock_resource "fabric_workspace_role_assignment" { 16 | defaults = { 17 | id = "00000000-0000-0000-0000-000000000000" 18 | workspace_id = "00000000-0000-0000-0000-000000000000" 19 | principal = { 20 | id = "00000000-0000-0000-0000-000000000000" 21 | type = "User" 22 | } 23 | role = "Admin" 24 | } 25 | } 26 | } 27 | 28 | run "testunit_301-testing-terraform" { 29 | providers = { 30 | fabric = fabric.fake 31 | } 32 | 33 | variables { 34 | workspace_name = "ws-test" 35 | workspace_description = "ws-test-description" 36 | principal = { 37 | id = "00000000-0000-0000-0000-000000000000" 38 | type = "User" 39 | } 40 | } 41 | 42 | command = apply 43 | 44 | assert { 45 | condition = fabric_workspace.example.display_name == "ws-test" 46 | error_message = "workspace display_name was not assigned correctly" 47 | } 48 | 49 | assert { 50 | condition = fabric_workspace.example.description == "ws-test-description" 51 | error_message = "workspace description was not assigned correctly" 52 | } 53 | 54 | assert { 55 | condition = fabric_workspace_role_assignment.example.workspace_id == fabric_workspace.example.id 56 | error_message = "workspace role was not assigned correctly" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /quickstarts/301-testing-terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "workspace_name" { 2 | description = "Name of the Workspace" 3 | type = string 4 | default = "test_workspace" 5 | } 6 | 7 | variable "workspace_description" { 8 | description = "Description of the Workspace" 9 | type = string 10 | default = "test_description" 11 | } 12 | 13 | variable "principal" { 14 | description = "Workspace role assignment Principal ID" 15 | type = object({ 16 | id = string 17 | type = string 18 | }) 19 | validation { 20 | condition = can(regex("^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$", var.principal.id)) 21 | error_message = "Please specify a valid principal ID." 22 | } 23 | 24 | validation { 25 | condition = contains(["User", "Group", "ServicePrincipal"], var.principal.type) 26 | error_message = "Please specify a valid principal type." 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /quickstarts/401-testing-terratest/README.md: -------------------------------------------------------------------------------- 1 | # Testing with Terratest (400 level) 2 | 3 | Automated tests can be written to verify Fabric resources deployment using the Fabric Terraform Provider. 4 | 5 | Checkout how to write automated unit tests against [301-testing-terraform](./../301-testing-terraform/) module using [Terratest](https://github.com/gruntwork-io/terratest) in the sample test in [workspace_test.go](./workspace_test.go). 6 | 7 | ## Output Values 8 | 9 | | Name | Description | 10 | |--------------------------------|------------------------------| 11 | | `workspace_id` | Workspace ID | 12 | | `workspace_role_assignment_id` | Workspace role assignment ID | 13 | 14 | ## Testing 15 | 16 | Running Terratest with GoLang tests: 17 | 18 | ```shell 19 | # Run all tests 20 | go test -v 21 | 22 | # Run selected test 23 | go test -v -run TestTerraform_Workspace 24 | ``` 25 | 26 | ## Limitations and Considerations 27 | 28 | - This example is provided as a sample only and is not intended for production use without further customization. 29 | -------------------------------------------------------------------------------- /quickstarts/401-testing-terratest/_header.md: -------------------------------------------------------------------------------- 1 | # Testing with Terratest (400 level) 2 | -------------------------------------------------------------------------------- /quickstarts/401-testing-terratest/go.mod: -------------------------------------------------------------------------------- 1 | module terratests 2 | 3 | go 1.24.3 4 | 5 | require ( 6 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 7 | github.com/gruntwork-io/terratest v0.49.0 8 | github.com/microsoft/fabric-sdk-go v0.6.0 9 | github.com/stretchr/testify v1.10.0 10 | ) 11 | 12 | require ( 13 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect 14 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect 15 | github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect 16 | github.com/agext/levenshtein v1.2.3 // indirect 17 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 18 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 21 | github.com/google/uuid v1.6.0 // indirect 22 | github.com/hashicorp/errwrap v1.0.0 // indirect 23 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 24 | github.com/hashicorp/go-getter/v2 v2.2.3 // indirect 25 | github.com/hashicorp/go-multierror v1.1.1 // indirect 26 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 27 | github.com/hashicorp/go-version v1.7.0 // indirect 28 | github.com/hashicorp/hcl/v2 v2.22.0 // indirect 29 | github.com/hashicorp/terraform-json v0.23.0 // indirect 30 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect 31 | github.com/klauspost/compress v1.16.5 // indirect 32 | github.com/kylelemons/godebug v1.1.0 // indirect 33 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect 34 | github.com/mitchellh/go-homedir v1.1.0 // indirect 35 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 36 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 37 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect 38 | github.com/pmezard/go-difflib v1.0.0 // indirect 39 | github.com/tmccombs/hcl2json v0.6.4 // indirect 40 | github.com/ulikunitz/xz v0.5.10 // indirect 41 | github.com/zclconf/go-cty v1.15.0 // indirect 42 | golang.org/x/crypto v0.38.0 // indirect 43 | golang.org/x/mod v0.18.0 // indirect 44 | golang.org/x/net v0.40.0 // indirect 45 | golang.org/x/sync v0.14.0 // indirect 46 | golang.org/x/sys v0.33.0 // indirect 47 | golang.org/x/text v0.25.0 // indirect 48 | golang.org/x/tools v0.22.0 // indirect 49 | gopkg.in/yaml.v3 v3.0.1 // indirect 50 | ) 51 | -------------------------------------------------------------------------------- /quickstarts/401-testing-terratest/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Azure/azure-sdk-for-go v51.0.0+incompatible h1:p7blnyJSjJqf5jflHbSGhIhEpXIgIFmYZNg5uwqweso= 2 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= 3 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= 4 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA= 5 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= 6 | github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= 7 | github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= 8 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= 9 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= 10 | github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= 11 | github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= 12 | github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= 13 | github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 14 | github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= 15 | github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 16 | github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= 17 | github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= 18 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= 19 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= 20 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 21 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 22 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 23 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 24 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 25 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 26 | github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= 27 | github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= 28 | github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 29 | github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 30 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 31 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 32 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 33 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 34 | github.com/gruntwork-io/terratest v0.49.0 h1:GurfpHEOEr8vntB77QcxDh+P7aiQRUgPFdgb6q9PuWI= 35 | github.com/gruntwork-io/terratest v0.49.0/go.mod h1:/+dfGio9NqUpvvukuPo29B8zy6U5FYJn9PdmvwztK4A= 36 | github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 37 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 38 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 39 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 40 | github.com/hashicorp/go-getter/v2 v2.2.3 h1:6CVzhT0KJQHqd9b0pK3xSP0CM/Cv+bVhk+jcaRJ2pGk= 41 | github.com/hashicorp/go-getter/v2 v2.2.3/go.mod h1:hp5Yy0GMQvwWVUmwLs3ygivz1JSLI323hdIE9J9m7TY= 42 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 43 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 44 | github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= 45 | github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= 46 | github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= 47 | github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 48 | github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M= 49 | github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= 50 | github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI= 51 | github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= 52 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= 53 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= 54 | github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= 55 | github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= 56 | github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= 57 | github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 58 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 59 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 60 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 61 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 62 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 63 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 64 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg= 65 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= 66 | github.com/microsoft/fabric-sdk-go v0.6.0 h1:cpJf00vfECqLCjsnDi2Dr/rqOQ8ylSxnyLuCc1BRSN4= 67 | github.com/microsoft/fabric-sdk-go v0.6.0/go.mod h1:1zql64r0BGvUqxRtvK9WzRMdX+fpBCNYIWBpcFRGJLk= 68 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 69 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 70 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 71 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 72 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= 73 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= 74 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= 75 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 76 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 77 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 78 | github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= 79 | github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= 80 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 81 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 82 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 83 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 84 | github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw= 85 | github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk= 86 | github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= 87 | github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 88 | github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ= 89 | github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= 90 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= 91 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= 92 | golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= 93 | golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= 94 | golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= 95 | golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 96 | golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= 97 | golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 98 | golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= 99 | golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 100 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 101 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 102 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 103 | golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= 104 | golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= 105 | golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= 106 | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 107 | golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= 108 | golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 109 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 110 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 111 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 112 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 113 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 114 | -------------------------------------------------------------------------------- /quickstarts/401-testing-terratest/workspace_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package terratests_test 5 | 6 | import ( 7 | "testing" 8 | 9 | "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 10 | "github.com/gruntwork-io/terratest/modules/terraform" 11 | fabcore "github.com/microsoft/fabric-sdk-go/fabric/core" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestTerraform_Workspace(t *testing.T) { 16 | t.Parallel() 17 | 18 | expectedWorkspaceName := "test-workspace" 19 | expectedWorkspaceDescription := "test-workspace-description" 20 | expectedRoleAssignmentPrincipalID := "bb978eea-7929-4d11-86e5-4c50a643e510" 21 | 22 | // Configure Terraform setting up a path to Terraform code. 23 | terraformOptions := &terraform.Options{ 24 | // The path to where our Terraform code is located 25 | TerraformDir: "./../301-testing-terraform", 26 | Vars: map[string]any{ 27 | "workspace_name": expectedWorkspaceName, 28 | "principal": map[string]any{ 29 | "id": expectedRoleAssignmentPrincipalID, 30 | "type": "ServicePrincipal", 31 | }, 32 | "workspace_description": expectedWorkspaceDescription, 33 | }, 34 | Reconfigure: true, 35 | } 36 | 37 | // Defer 'terraform Destroy' 38 | defer terraform.Destroy(t, terraformOptions) 39 | 40 | // Run `terraform Init` to initialize Terraform and download the Terraform Provider for Fabric 41 | terraform.Init(t, terraformOptions) 42 | 43 | // Run `terraform Apply` to deploy resources. 44 | terraform.Apply(t, terraformOptions) 45 | 46 | // Run `terraform output` to read the expected values out of the terraform.tfstate output 47 | expectedWorkspaceID := terraform.Output(t, terraformOptions, "workspace_id") 48 | expectedWorkspaceRoleAssignmentID := terraform.Output(t, terraformOptions, "workspace_role_assignment_id") 49 | 50 | // Get actual values from the SDK 51 | credentials, _ := azidentity.NewDefaultAzureCredential(&azidentity.DefaultAzureCredentialOptions{}) 52 | client, _ := fabcore.NewClientFactory(credentials, nil, nil) 53 | 54 | workspaceClient := client.NewWorkspacesClient() 55 | 56 | actualWorkspace, _ := workspaceClient.GetWorkspace(t.Context(), expectedWorkspaceID, nil) 57 | actualWorkspaceRoleAssignment, _ := workspaceClient.GetWorkspaceRoleAssignment(t.Context(), expectedWorkspaceID, expectedWorkspaceRoleAssignmentID, nil) 58 | 59 | // Assert workspace values 60 | assert.Equal(t, expectedWorkspaceName, *actualWorkspace.DisplayName, "workspace display_name was not assigned correctly") 61 | assert.Equal(t, expectedWorkspaceDescription, *actualWorkspace.Description, "workspace description was not assigned correctly") 62 | 63 | // Assert workspace role assignment values 64 | assert.Equal(t, expectedWorkspaceRoleAssignmentID, *actualWorkspaceRoleAssignment.ID, "workspace role was not assigned correctly") 65 | } 66 | -------------------------------------------------------------------------------- /tests/mocks/azuread/data.tfmock.hcl: -------------------------------------------------------------------------------- 1 | mock_data "azuread_directory_object" { 2 | defaults = { 3 | id = "/directoryObjects/00000000-0000-0000-0000-000000000000" 4 | object_id = "00000000-0000-0000-0000-000000000000" 5 | type = "User" 6 | } 7 | } 8 | 9 | mock_data "azuread_user" { 10 | defaults = { 11 | id = "/users/00000000-0000-0000-0000-000000000000" 12 | object_id = "00000000-0000-0000-0000-000000000000" 13 | display_name = "Test User" 14 | mail_nickname = "User" 15 | user_principal_name = "User@example.onmicrosoft.com" 16 | user_type = "Member" 17 | preferred_language = "en-US" 18 | usage_location = "US" 19 | account_enabled = true 20 | } 21 | } 22 | 23 | mock_data "azuread_group" { 24 | defaults = { 25 | id = "/groups/00000000-0000-0000-0000-000000000000" 26 | description = "" 27 | display_name = "Test Security Group" 28 | object_id = "00000000-0000-0000-0000-000000000000" 29 | mail_nickname = "sg-test" 30 | security_enabled = true 31 | mail_enabled = false 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/mocks/azurerm/data.tfmock.hcl: -------------------------------------------------------------------------------- 1 | mock_data "azurerm_client_config" { 2 | defaults = { 3 | id = "Y2xpZW50Q29uZmlncy9jbGllbnRJZD0wMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDA7b2JqZWN0SWQ9MDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwO3N1YnNjcmlwdGlvbklkPTAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMDt0ZW5hbnRJZD0wMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDA=" # clientConfigs/clientId=00000000-0000-0000-0000-000000000000;objectId=00000000-0000-0000-0000-000000000000;subscriptionId=00000000-0000-0000-0000-000000000000;tenantId=00000000-0000-0000-0000-000000000000 4 | client_id = "00000000-0000-0000-0000-000000000000" 5 | object_id = "00000000-0000-0000-0000-000000000000" 6 | subscription_id = "00000000-0000-0000-0000-000000000000" 7 | tenant_id = "00000000-0000-0000-0000-000000000000" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/mocks/azurerm/resource.tfmock.hcl: -------------------------------------------------------------------------------- 1 | mock_resource "azurerm_resource_group" { 2 | defaults = { 3 | id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test" 4 | name = "rg-test" 5 | location = "westus2" 6 | tags = null 7 | managed_by = null 8 | } 9 | } 10 | 11 | mock_resource "azurerm_fabric_capacity" { 12 | defaults = { 13 | id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Fabric/capacities/fctest" 14 | location = "westus2" 15 | administration_members = [ 16 | "User@example.onmicrosoft.com", 17 | "example@example.onmicrosoft.com", 18 | ] 19 | name = "fctest" 20 | resource_group_name = "rg-test" 21 | sku = [{ 22 | name = "F4" 23 | tier = "Fabric" 24 | }] 25 | tags = null 26 | } 27 | } 28 | 29 | mock_resource "azurerm_virtual_network" { 30 | defaults = { 31 | id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/virtualNetworks/vnet-test" 32 | location = "westus2" 33 | name = "vnet-test" 34 | resource_group_name = "rg-test" 35 | private_endpoint_vnet_policies = "Disabled" 36 | address_space = [ 37 | "10.0.0.0/16", 38 | ] 39 | } 40 | } 41 | 42 | mock_resource "azurerm_subnet" { 43 | defaults = { 44 | id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/virtualNetworks/vnet-test/subnets/snet-default" 45 | address_prefixes = [ 46 | "10.0.1.0/24", 47 | ] 48 | name = "snet-default" 49 | private_endpoint_network_policies = "Disabled" 50 | private_link_service_network_policies_enabled = true 51 | resource_group_name = "rg-test" 52 | virtual_network_name = "vnet-test" 53 | delegation = [ 54 | { 55 | name = "delegation" 56 | service_delegation = [ 57 | { 58 | actions = [ 59 | "Microsoft.Network/virtualNetworks/subnets/join/action", 60 | ] 61 | name = "Microsoft.PowerPlatform/vnetaccesslinks" 62 | }, 63 | ] 64 | }, 65 | ] 66 | } 67 | } 68 | 69 | mock_resource "azurerm_network_security_group" { 70 | defaults = { 71 | id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/networkSecurityGroups/nsg-default" 72 | location = "westus2" 73 | name = "nsg-default" 74 | resource_group_name = "rg-test" 75 | } 76 | } 77 | 78 | mock_resource "azurerm_subnet_network_security_group_association" { 79 | defaults = { 80 | id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/virtualNetworks/vnet-test/subnets/snet-default/providers/Microsoft.Network/networkSecurityGroups/nsg-default" 81 | subnet_id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/virtualNetworks/vnet-test/subnets/snet-default" 82 | network_security_group_id = "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-test/providers/Microsoft.Network/networkSecurityGroups/nsg-default" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/mocks/fabric/data.tfmock.hcl: -------------------------------------------------------------------------------- 1 | mock_data "fabric_capacity" { 2 | defaults = { 3 | display_name = "fctest" 4 | id = "00000000-0000-0000-0000-000000000000" 5 | region = "westus2" 6 | sku = "F4" 7 | state = "Active" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/mocks/fabric/resource.tfmock.hcl: -------------------------------------------------------------------------------- 1 | mock_resource "fabric_workspace" { 2 | defaults = { 3 | id = "00000000-0000-0000-0000-000000000000" 4 | display_name = "ws-test" 5 | capacity_id = "00000000-0000-0000-0000-000000000000" 6 | capacity_region = "westus2" 7 | description = "" 8 | identity = null 9 | } 10 | } 11 | 12 | mock_resource "fabric_gateway" { 13 | defaults = { 14 | capacity_id = "00000000-0000-0000-0000-000000000000" 15 | display_name = "gw-test" 16 | id = "00000000-0000-0000-0000-000000000000" 17 | inactivity_minutes_before_sleep = 30 18 | number_of_member_gateways = 1 19 | type = "VirtualNetwork" 20 | virtual_network_azure_resource = { 21 | resource_group_name = "rg-test" 22 | virtual_network_name = "vnet-test" 23 | subnet_name = "subnet-test" 24 | subscription_id = "00000000-0000-0000-0000-000000000000" 25 | } 26 | } 27 | } 28 | 29 | mock_resource "fabric_gateway_role_assignment" { 30 | defaults = { 31 | id = "00000000-0000-0000-0000-000000000000" 32 | gateway_id = "00000000-0000-0000-0000-000000000000" 33 | principal = { 34 | id = "00000000-0000-0000-0000-000000000000" 35 | type = "Group" 36 | } 37 | role = "Admin" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/random_generator/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Random Generator (test helper) 3 | 4 | --- 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |-----------|----------------| 10 | | terraform | >= 1.11, < 2.0 | 11 | | random | 3.7.2 | 12 | 13 | ## Providers 14 | 15 | | Name | Version | 16 | |--------|---------| 17 | | random | 3.7.2 | 18 | 19 | ## Modules 20 | 21 | No modules. 22 | 23 | ## Resources 24 | 25 | | Name | Type | 26 | |----------------------------------------------------------------------------------------------------------------|----------| 27 | | [random_password.this](https://registry.terraform.io/providers/hashicorp/random/3.7.2/docs/resources/password) | resource | 28 | | [random_string.this](https://registry.terraform.io/providers/hashicorp/random/3.7.2/docs/resources/string) | resource | 29 | | [random_uuid.this](https://registry.terraform.io/providers/hashicorp/random/3.7.2/docs/resources/uuid) | resource | 30 | 31 | ## Inputs 32 | 33 | | Name | Description | Type | Default | Required | 34 | |--------|-----------------------------|----------|---------|:--------:| 35 | | length | Length of the random string | `number` | `16` | no | 36 | 37 | ## Outputs 38 | 39 | | Name | Description | 40 | |----------|------------------------------------------------------------| 41 | | password | Random password generated by the random\_password resource | 42 | | string | Random string generated by the random\_string resource | 43 | | uuid | Random UUID generated by the random\_uuid resource | 44 | 45 | ## Usage 46 | 47 | How to use in tests? 48 | 49 | ```hcl 50 | run "random" { 51 | module { 52 | source = "../../tests/random_generator" 53 | } 54 | } 55 | 56 | run "testacc_example" { 57 | variables { 58 | my_var = "foo-${run.random.string}-bar" 59 | } 60 | } 61 | ``` 62 | 63 | ## Limitations and Considerations 64 | 65 | - This example is provided as a sample only and is not intended for production use without further customization. 66 | -------------------------------------------------------------------------------- /tests/random_generator/_footer.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | How to use in tests? 4 | 5 | ```hcl 6 | run "random" { 7 | module { 8 | source = "../../tests/random_generator" 9 | } 10 | } 11 | 12 | run "testacc_example" { 13 | variables { 14 | my_var = "foo-${run.random.string}-bar" 15 | } 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /tests/random_generator/_header.md: -------------------------------------------------------------------------------- 1 | # Random Generator (test helper) 2 | -------------------------------------------------------------------------------- /tests/random_generator/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.11, < 2.0" 3 | required_providers { 4 | # https://registry.terraform.io/providers/hashicorp/random/latest 5 | random = { 6 | source = "hashicorp/random" 7 | version = "3.7.2" 8 | } 9 | } 10 | } 11 | 12 | resource "random_string" "this" { 13 | keepers = { 14 | first = timestamp() 15 | } 16 | length = var.length 17 | special = false 18 | numeric = false 19 | } 20 | 21 | resource "random_uuid" "this" { 22 | keepers = { 23 | first = timestamp() 24 | } 25 | } 26 | 27 | resource "random_password" "this" { 28 | keepers = { 29 | first = timestamp() 30 | } 31 | length = var.length 32 | special = true 33 | override_special = "!#$%&*()-_=+[]{}<>:?" 34 | } 35 | 36 | -------------------------------------------------------------------------------- /tests/random_generator/outputs.tf: -------------------------------------------------------------------------------- 1 | output "string" { 2 | value = random_string.this.result 3 | description = "Random string generated by the random_string resource" 4 | } 5 | 6 | output "uuid" { 7 | value = random_uuid.this.result 8 | description = "Random UUID generated by the random_uuid resource" 9 | } 10 | 11 | output "password" { 12 | value = random_password.this.result 13 | sensitive = true 14 | description = "Random password generated by the random_password resource" 15 | } 16 | -------------------------------------------------------------------------------- /tests/random_generator/tests/test_unit.tftest.hcl: -------------------------------------------------------------------------------- 1 | run "testunit_random_generator" { 2 | command = apply 3 | 4 | assert { 5 | condition = length(random_string.this.result) != 0 6 | error_message = "random string was not assigned correctly" 7 | } 8 | 9 | assert { 10 | condition = length(random_uuid.this.result) != 0 11 | error_message = "random uuid was not assigned correctly" 12 | } 13 | 14 | assert { 15 | condition = length(random_password.this.result) != 0 16 | error_message = "random password was not assigned correctly" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/random_generator/variables.tf: -------------------------------------------------------------------------------- 1 | variable "length" { 2 | type = number 3 | description = "Length of the random string" 4 | default = 16 5 | } 6 | --------------------------------------------------------------------------------