Skip to content

Commit e1e60cc

Browse files
authored
@onlyIfNotExists example (#34)
* Add @onlyIfNotExists decorator example
1 parent bd8ec0f commit e1e60cc

File tree

3 files changed

+141
-2
lines changed

3 files changed

+141
-2
lines changed

.github/workflows/mega-linter.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ jobs:
4141
id: ml
4242
# You can override MegaLinter flavor used to have faster performances
4343
# More info at https://megalinter.io/flavors/
44-
uses: oxsecurity/megalinter@v8
44+
uses: oxsecurity/megalinter@v9
4545
env:
4646
# All available variables are described in documentation
4747
# https://megalinter.io/configuration/
4848
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
4949
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5050
# ADD YOUR CUSTOM ENV VARIABLES HERE OR DEFINE THEM IN A FILE .mega-linter.yml AT THE ROOT OF YOUR REPOSITORY
5151
DISABLE: COPYPASTE,SPELL # Uncomment to disable copy-paste and spell checks
52-
DISABLE_LINTERS: YAML_V8R,YAML_YAMLLINT,YAML_PRETTIER,JSON_PRETTIER,REPOSITORY_CHECKOV,POWERSHELL_POWERSHELL,ACTION_ACTIONLINT,REPOSITORY_GITLEAKS,REPOSITORY_GRYPE,REPOSITORY_KICS,REPOSITORY_TRIVY
52+
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
5353

5454
# Upload MegaLinter artifacts
5555
- name: Archive production artifacts
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Azure Bicep - @onlyIfNotExists Decorator
2+
3+
## Introduction
4+
5+
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.
6+
7+
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.
8+
9+
> [!NOTE]
10+
> 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.
11+
12+
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).
13+
14+
## ✅ Benefits of Using @onlyIfNotExists
15+
16+
**Prevents Overwriting Critical Data**: Protects existing resources (like secrets with sensitive values) from being accidentally overwritten during redeployment.
17+
18+
**Idempotent Deployments**: Enables truly idempotent templates where multiple executions won't fail or cause unintended changes if resources already exist.
19+
20+
**Simplified Secret Management**: Perfect for one-time secret deployments where you want to set an initial value but never modify it through automation afterward.
21+
22+
**Reduces Deployment Errors**: Eliminates "resource already exists" conflicts in scenarios where you're uncertain whether a resource has been previously deployed.
23+
24+
## ⚗️ Example: Key Vault Secret with @onlyIfNotExists
25+
26+
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**.
27+
28+
### Bicep Code Snippet
29+
30+
```bicep
31+
@secure()
32+
param kvSecretValue string
33+
34+
// Deploy secret to the Key Vault using @onlyIfNotExists decorator
35+
@onlyIfNotExists()
36+
resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
37+
name: '${keyVault.outputs.name}/mySecret'
38+
properties: {
39+
value: kvSecretValue
40+
}
41+
}
42+
```
43+
44+
### How It Works
45+
46+
1. **First Deployment**: The secret `mySecret` doesn't exist, so it's created with the value you pass at deployment time
47+
2. **Subsequent Deployments**: The secret already exists with the same name, so deployment is skipped entirely - the existing value is preserved
48+
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.)
49+
50+
### Full Template
51+
52+
The complete `main.bicep` file includes:
53+
54+
- Azure Key Vault deployment using the Azure Verified Modules (AVM) pattern
55+
- A secret resource with the `@onlyIfNotExists()` decorator
56+
- Metadata for resource documentation
57+
- Outputs for the Key Vault name, URI, and secret name
58+
59+
### Scope Limitations
60+
61+
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.
62+
63+
## 🚀 Deployment
64+
65+
### Prerequisites
66+
67+
- Azure subscription
68+
- Bicep CLI 0.38.0 or later
69+
70+
### Deploy with Azure CLI
71+
72+
```bash
73+
az group create --name rg-onlyIfNotExists-example --location uksouth
74+
75+
az deployment group create \
76+
--resource-group rg-onlyIfNotExists-example \
77+
--template-file main.bicep \
78+
--parameters kvSecretValue="<your-secret-value>"
79+
```
80+
81+
### Deploy with Azure PowerShell
82+
83+
```powershell
84+
New-AzResourceGroup -Name rg-onlyIfNotExists-example -Location uksouth
85+
86+
New-AzResourceGroupDeployment `
87+
-ResourceGroupName rg-onlyIfNotExists-example `
88+
-TemplateFile main.bicep `
89+
-kvSecretValue '<your-secret-value>'
90+
```
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
targetScope = 'resourceGroup'
2+
3+
metadata name = 'Key Vault with onlyIfNotExists Secret deployment'
4+
metadata description = 'Showcasing Azure Bicep @onlyIfNotExists decorator for conditional secret deployment'
5+
6+
@description('Azure region for deployments chosen from the resource group.')
7+
param location string = resourceGroup().location
8+
9+
@description('Key Vault name - globally unique.')
10+
param kvName string = 'kv-${uniqueString(resourceGroup().id)}'
11+
12+
@secure()
13+
@description('Initial value for the Key Vault secret. Passed in at deploy time to avoid hardcoding secrets.')
14+
param kvSecretValue string
15+
16+
// Deploy Key Vault using AVM
17+
module keyVault 'br/public:avm/res/key-vault/vault:0.13.3' = {
18+
name: '${uniqueString(deployment().name, location)}-kv'
19+
params: {
20+
name: kvName
21+
location: location
22+
sku: 'standard'
23+
enableRbacAuthorization: false
24+
enableSoftDelete: true
25+
softDeleteRetentionInDays: 7
26+
enablePurgeProtection: false
27+
}
28+
}
29+
30+
// Deploy secret to the Key Vault using @onlyIfNotExists decorator
31+
@onlyIfNotExists()
32+
resource kvSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
33+
name: '${kvName}/mySecret'
34+
properties: {
35+
value: kvSecretValue
36+
}
37+
dependsOn: [
38+
keyVault
39+
]
40+
}
41+
42+
@description('Key Vault name output.')
43+
output keyVaultName string = keyVault.outputs.name
44+
45+
@description('Key Vault URI output.')
46+
output keyVaultUri string = keyVault.outputs.uri
47+
48+
@description('Secret name output.')
49+
output secretName string = last(split(kvSecret.name, '/'))

0 commit comments

Comments
 (0)