Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/mega-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ 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/
VALIDATE_ALL_CODEBASE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} # Validates all source when push on main, else just the git diff with main. Override with true if you always want to lint all sources
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
Expand Down
90 changes: 90 additions & 0 deletions bicep-examples/onlyIfNotExists/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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.

> [!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).

## ✅ 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
@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: kvSecretValue
}
}
```

### How It Works

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.)

### 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

### 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 \
--parameters kvSecretValue="<your-secret-value>"
```

### Deploy with Azure PowerShell

```powershell
New-AzResourceGroup -Name rg-onlyIfNotExists-example -Location uksouth

New-AzResourceGroupDeployment `
-ResourceGroupName rg-onlyIfNotExists-example `
-TemplateFile main.bicep `
-kvSecretValue '<your-secret-value>'
```
49 changes: 49 additions & 0 deletions bicep-examples/onlyIfNotExists/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
targetScope = 'resourceGroup'

metadata name = 'Key Vault with onlyIfNotExists Secret deployment'
metadata description = 'Showcasing Azure Bicep @onlyIfNotExists decorator for conditional secret deployment'

@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)}'

@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'
params: {
name: kvName
location: location
sku: 'standard'
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: '${kvName}/mySecret'
properties: {
value: kvSecretValue
}
dependsOn: [
keyVault
]
}

@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, '/'))