From 10300293b3ff53bdefc6d7c38992a083e029cd4b Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Sun, 11 Jan 2026 21:08:06 +0000 Subject: [PATCH 1/4] Add README and main.bicep files for @onlyIfNotExists decorator example --- bicep-examples/onlyIfNotExists/README.md | 106 ++++++++++++++++++++++ bicep-examples/onlyIfNotExists/main.bicep | 42 +++++++++ 2 files changed, 148 insertions(+) create mode 100644 bicep-examples/onlyIfNotExists/README.md create mode 100644 bicep-examples/onlyIfNotExists/main.bicep diff --git a/bicep-examples/onlyIfNotExists/README.md b/bicep-examples/onlyIfNotExists/README.md new file mode 100644 index 0000000..eb3ffb8 --- /dev/null +++ b/bicep-examples/onlyIfNotExists/README.md @@ -0,0 +1,106 @@ +# Azure Bicep - @onlyIfNotExists Decorator + +## Introduction + +The `@onlyIfNotExists()` decorator in Azure Bicep enables **conditional resource deployment** based on whether a resource with the specified name already exists. When applied to a resource declaration, Azure Resource Manager will only create the resource if one with that name doesn't already exist in the target scope. + +It's particularly valuable for deploying resources that should only be created once, such as Key Vault secrets, where you want to avoid overwriting existing values or encountering "resource already exists" errors. + +**Key Concept**: The decorator checks only the resource **name** - it does not compare properties or configurations. If a resource with the same name exists, deployment is skipped entirely for that resource, regardless of whether its properties match the template definition. + +Learn more about the `@onlyIfNotExists()` decorator in the [official Microsoft Learn documentation](https://learn.microsoft.com/azure/azure-resource-manager/bicep/resource-declaration?WT.mc_id=MVP_319025#onlyifnotexists-decorator). + +## ✅ Benefits of Using @onlyIfNotExists + +✅ **Prevents Overwriting Critical Data**: Protects existing resources (like secrets with sensitive values) from being accidentally overwritten during redeployment. + +✅ **Idempotent Deployments**: Enables truly idempotent templates where multiple executions won't fail or cause unintended changes if resources already exist. + +✅ **Simplified Secret Management**: Perfect for one-time secret deployments where you want to set an initial value but never modify it through automation afterward. + +✅ **Reduces Deployment Errors**: Eliminates "resource already exists" conflicts in scenarios where you're uncertain whether a resource has been previously deployed. + +## ⚗️ Example: Key Vault Secret with @onlyIfNotExists + +This example demonstrates deploying an Azure Key Vault and a secret using the `@onlyIfNotExists()` decorator. The Key Vault will always be deployed (or updated), but the secret will **only be created if it doesn't already exist**. + +### Bicep Code Snippet + +```bicep +// Deploy secret to the Key Vault using @onlyIfNotExists decorator +@onlyIfNotExists() +resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + name: '${keyVault.outputs.name}/mySecret' + properties: { + value: 'SecureValue123!' + } +} +``` + +### How It Works + +1. **First Deployment**: The secret `mySecret` doesn't exist, so it's created with the value `SecureValue123!` +2. **Subsequent Deployments**: The secret already exists with the same name, so deployment is skipped entirely - the existing value is preserved +3. **Name-Based Check**: Only the secret name is checked; if a secret named `mySecret` exists, it won't be recreated regardless of its current value + +### Full Template + +The complete `main.bicep` file includes: +- Azure Key Vault deployment using the Azure Verified Modules (AVM) pattern +- A secret resource with the `@onlyIfNotExists()` decorator +- Metadata for resource documentation +- Outputs for the Key Vault name, URI, and secret name + +## ⚠️ Important Considerations + +### Decorator Placement +The `@onlyIfNotExists()` decorator must be placed **directly above** the resource declaration: + +```bicep +@onlyIfNotExists() +resource myResource 'Microsoft.Provider/type@version' = { ... } +``` + +### Bicep CLI Version Requirement +Requires **Bicep CLI 0.38.0 or later**. Check your version: + +```bash +bicep --version +``` + +### Name-Based Detection Only +The decorator **only checks if a resource with the specified name exists**. It does not: +- Compare resource properties or configurations +- Detect if the existing resource matches your template definition +- Update existing resources with new property values + +### Scope Limitations + +The existence check is performed within the deployment scope (subscription, resource group, etc.). A resource in a different scope with the same name won't prevent creation. + +## 🚀 Deployment + +### Prerequisites + +- Azure subscription +- Bicep CLI 0.38.0 or later + +### Deploy with Azure CLI + +```bash +az group create --name rg-onlyIfNotExists-example --location uksouth + +az deployment group create \ + --resource-group rg-onlyIfNotExists-example \ + --template-file main.bicep +``` + +### Deploy with Azure PowerShell + +```powershell +New-AzResourceGroup -Name rg-onlyIfNotExists-example -Location uksouth + +New-AzResourceGroupDeployment ` + -ResourceGroupName rg-onlyIfNotExists-example ` + -TemplateFile main.bicep +``` diff --git a/bicep-examples/onlyIfNotExists/main.bicep b/bicep-examples/onlyIfNotExists/main.bicep new file mode 100644 index 0000000..eb7d9bc --- /dev/null +++ b/bicep-examples/onlyIfNotExists/main.bicep @@ -0,0 +1,42 @@ +targetScope = 'resourceGroup' + +metadata name = 'Key Vault with onlyIfNotExists Secret deployment' +metadata description = 'Showcasing Azure Bicep @onlyIfNotExists decorator for conditional secret deployment' +metadata owner = 'security@example.com' + +@description('Azure region for deployments chosen from the resource group.') +param location string = resourceGroup().location + +@description('Key Vault name - globally unique.') +param kvName string = 'kv-${uniqueString(resourceGroup().id)}' + +// Deploy Key Vault using AVM +module keyVault 'br/public:avm/res/key-vault/vault:0.13.3' = { + name: '${uniqueString(deployment().name, location)}-kv' + params: { + name: kvName + location: location + sku: 'standard' + enableRbacAuthorization: false + enableSoftDelete: true + softDeleteRetentionInDays: 7 + } +} + +// Deploy secret to the Key Vault using @onlyIfNotExists decorator +@onlyIfNotExists() +resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { + name: '${keyVault.outputs.name}/mySecret' + properties: { + value: 'SecureValue123!' + } +} + +@description('Key Vault name output.') +output keyVaultName string = keyVault.outputs.name + +@description('Key Vault URI output.') +output keyVaultUri string = keyVault.outputs.uri + +@description('Secret name output.') +output secretName string = last(split(kvSecret.name, '/')) From 59641f1fd13fdf2e13248bef5f6679aa2a275416 Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Sun, 11 Jan 2026 21:18:28 +0000 Subject: [PATCH 2/4] Refactor README and main.bicep for @onlyIfNotExists example: update notes and fix secret name reference --- bicep-examples/onlyIfNotExists/README.md | 29 ++++------------------- bicep-examples/onlyIfNotExists/main.bicep | 7 ++++-- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/bicep-examples/onlyIfNotExists/README.md b/bicep-examples/onlyIfNotExists/README.md index eb3ffb8..aee415c 100644 --- a/bicep-examples/onlyIfNotExists/README.md +++ b/bicep-examples/onlyIfNotExists/README.md @@ -6,7 +6,8 @@ The `@onlyIfNotExists()` decorator in Azure Bicep enables **conditional resource It's particularly valuable for deploying resources that should only be created once, such as Key Vault secrets, where you want to avoid overwriting existing values or encountering "resource already exists" errors. -**Key Concept**: The decorator checks only the resource **name** - it does not compare properties or configurations. If a resource with the same name exists, deployment is skipped entirely for that resource, regardless of whether its properties match the template definition. +> [!NOTE] +> The decorator checks only the resource **name** - it does not compare properties or configurations. If a resource with the same name exists, deployment is skipped entirely for that resource, regardless of whether its properties match the template definition. Learn more about the `@onlyIfNotExists()` decorator in the [official Microsoft Learn documentation](https://learn.microsoft.com/azure/azure-resource-manager/bicep/resource-declaration?WT.mc_id=MVP_319025#onlyifnotexists-decorator). @@ -41,39 +42,17 @@ resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { 1. **First Deployment**: The secret `mySecret` doesn't exist, so it's created with the value `SecureValue123!` 2. **Subsequent Deployments**: The secret already exists with the same name, so deployment is skipped entirely - the existing value is preserved -3. **Name-Based Check**: Only the secret name is checked; if a secret named `mySecret` exists, it won't be recreated regardless of its current value +3. **Name-Based Check**: Only the secret name is checked; if a secret named `mySecret` exists, it won't be recreated regardless of its current value (check the secret version after two deployment runs to verify functionality.) ### Full Template The complete `main.bicep` file includes: + - Azure Key Vault deployment using the Azure Verified Modules (AVM) pattern - A secret resource with the `@onlyIfNotExists()` decorator - Metadata for resource documentation - Outputs for the Key Vault name, URI, and secret name -## ⚠️ Important Considerations - -### Decorator Placement -The `@onlyIfNotExists()` decorator must be placed **directly above** the resource declaration: - -```bicep -@onlyIfNotExists() -resource myResource 'Microsoft.Provider/type@version' = { ... } -``` - -### Bicep CLI Version Requirement -Requires **Bicep CLI 0.38.0 or later**. Check your version: - -```bash -bicep --version -``` - -### Name-Based Detection Only -The decorator **only checks if a resource with the specified name exists**. It does not: -- Compare resource properties or configurations -- Detect if the existing resource matches your template definition -- Update existing resources with new property values - ### Scope Limitations The existence check is performed within the deployment scope (subscription, resource group, etc.). A resource in a different scope with the same name won't prevent creation. diff --git a/bicep-examples/onlyIfNotExists/main.bicep b/bicep-examples/onlyIfNotExists/main.bicep index eb7d9bc..5f75c39 100644 --- a/bicep-examples/onlyIfNotExists/main.bicep +++ b/bicep-examples/onlyIfNotExists/main.bicep @@ -2,7 +2,6 @@ targetScope = 'resourceGroup' metadata name = 'Key Vault with onlyIfNotExists Secret deployment' metadata description = 'Showcasing Azure Bicep @onlyIfNotExists decorator for conditional secret deployment' -metadata owner = 'security@example.com' @description('Azure region for deployments chosen from the resource group.') param location string = resourceGroup().location @@ -20,16 +19,20 @@ module keyVault 'br/public:avm/res/key-vault/vault:0.13.3' = { enableRbacAuthorization: false enableSoftDelete: true softDeleteRetentionInDays: 7 + enablePurgeProtection: false } } // Deploy secret to the Key Vault using @onlyIfNotExists decorator @onlyIfNotExists() resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { - name: '${keyVault.outputs.name}/mySecret' + name: '${kvName}/mySecret' properties: { value: 'SecureValue123!' } + dependsOn: [ + keyVault + ] } @description('Key Vault name output.') From 1aee689d4e45d45ad5e596b60b55e8e9540b1516 Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:17:36 +0000 Subject: [PATCH 3/4] Update examples for @onlyIfNotExists: parameterize secret value and upgrade MegaLinter to v9 --- .github/workflows/mega-linter.yml | 2 +- bicep-examples/onlyIfNotExists/README.md | 13 +++++++++---- bicep-examples/onlyIfNotExists/main.bicep | 6 +++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml index c86a227..599f54c 100644 --- a/.github/workflows/mega-linter.yml +++ b/.github/workflows/mega-linter.yml @@ -41,7 +41,7 @@ jobs: id: ml # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.io/flavors/ - uses: oxsecurity/megalinter@v8 + uses: oxsecurity/megalinter@v9 env: # All available variables are described in documentation # https://megalinter.io/configuration/ diff --git a/bicep-examples/onlyIfNotExists/README.md b/bicep-examples/onlyIfNotExists/README.md index aee415c..8b6bbeb 100644 --- a/bicep-examples/onlyIfNotExists/README.md +++ b/bicep-examples/onlyIfNotExists/README.md @@ -28,19 +28,22 @@ This example demonstrates deploying an Azure Key Vault and a secret using the `@ ### Bicep Code Snippet ```bicep +@secure() +param kvSecretValue string + // Deploy secret to the Key Vault using @onlyIfNotExists decorator @onlyIfNotExists() resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { name: '${keyVault.outputs.name}/mySecret' properties: { - value: 'SecureValue123!' + value: kvSecretValue } } ``` ### How It Works -1. **First Deployment**: The secret `mySecret` doesn't exist, so it's created with the value `SecureValue123!` +1. **First Deployment**: The secret `mySecret` doesn't exist, so it's created with the value you pass at deployment time 2. **Subsequent Deployments**: The secret already exists with the same name, so deployment is skipped entirely - the existing value is preserved 3. **Name-Based Check**: Only the secret name is checked; if a secret named `mySecret` exists, it won't be recreated regardless of its current value (check the secret version after two deployment runs to verify functionality.) @@ -71,7 +74,8 @@ az group create --name rg-onlyIfNotExists-example --location uksouth az deployment group create \ --resource-group rg-onlyIfNotExists-example \ - --template-file main.bicep + --template-file main.bicep \ + --parameters kvSecretValue="" ``` ### Deploy with Azure PowerShell @@ -81,5 +85,6 @@ New-AzResourceGroup -Name rg-onlyIfNotExists-example -Location uksouth New-AzResourceGroupDeployment ` -ResourceGroupName rg-onlyIfNotExists-example ` - -TemplateFile main.bicep + -TemplateFile main.bicep ` + -kvSecretValue '' ``` diff --git a/bicep-examples/onlyIfNotExists/main.bicep b/bicep-examples/onlyIfNotExists/main.bicep index 5f75c39..150b950 100644 --- a/bicep-examples/onlyIfNotExists/main.bicep +++ b/bicep-examples/onlyIfNotExists/main.bicep @@ -9,6 +9,10 @@ param location string = resourceGroup().location @description('Key Vault name - globally unique.') param kvName string = 'kv-${uniqueString(resourceGroup().id)}' +@secure() +@description('Initial value for the Key Vault secret. Passed in at deploy time to avoid hardcoding secrets.') +param kvSecretValue string + // Deploy Key Vault using AVM module keyVault 'br/public:avm/res/key-vault/vault:0.13.3' = { name: '${uniqueString(deployment().name, location)}-kv' @@ -28,7 +32,7 @@ module keyVault 'br/public:avm/res/key-vault/vault:0.13.3' = { resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = { name: '${kvName}/mySecret' properties: { - value: 'SecureValue123!' + value: kvSecretValue } dependsOn: [ keyVault From 6ee0db14b62db7d792cce717a4bb0522229b8b8d Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:22:22 +0000 Subject: [PATCH 4/4] Update mega-linter.yml to disable additional linters: add KINGFISHER to DISABLE_LINTERS --- .github/workflows/mega-linter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml index 599f54c..8a2198f 100644 --- a/.github/workflows/mega-linter.yml +++ b/.github/workflows/mega-linter.yml @@ -49,7 +49,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # ADD YOUR CUSTOM ENV VARIABLES HERE OR DEFINE THEM IN A FILE .mega-linter.yml AT THE ROOT OF YOUR REPOSITORY DISABLE: COPYPASTE,SPELL # Uncomment to disable copy-paste and spell checks - DISABLE_LINTERS: YAML_V8R,YAML_YAMLLINT,YAML_PRETTIER,JSON_PRETTIER,REPOSITORY_CHECKOV,POWERSHELL_POWERSHELL,ACTION_ACTIONLINT,REPOSITORY_GITLEAKS,REPOSITORY_GRYPE,REPOSITORY_KICS,REPOSITORY_TRIVY + DISABLE_LINTERS: YAML_V8R,YAML_YAMLLINT,YAML_PRETTIER,JSON_PRETTIER,REPOSITORY_CHECKOV,POWERSHELL_POWERSHELL,ACTION_ACTIONLINT,REPOSITORY_GITLEAKS,REPOSITORY_GRYPE,REPOSITORY_KICS,REPOSITORY_TRIVY,REPOSITORY_KINGFISHER # Upload MegaLinter artifacts - name: Archive production artifacts