How can I adapt an existing resource with Azure Bicep? - azure

I'm currently porting some infrastructure as code scripts from Azure CLI to Azure Bicep. Among many other things, the Bicep files should create a subnet and allow access from this subnet to an existing Azure SQL Server and an existing Storage Account.
For the SQL Server, this is simple - I can reference the existing server resource and declare a child resource representing the VNET rule:
resource azureSqlServer 'Microsoft.Sql/servers#2021-05-01-preview' existing = {
name: azureSqlServerName
resource vnetRule 'virtualNetworkRules' = {
name: azureSqlServerVnetRuleName
properties: {
virtualNetworkSubnetId: subnetId
}
}
}
However, with the Storage Account, the network rules are not child resources, but a property of the Storage Account resource (properties.networkAcls.virtualNetworkRules). I cannot declare all the details of the Storage Account in my Bicep file because that resource is way out of scope from the deployment I'm currently working on. In essence, I want to adapt the existing resource, just ensuring a single rule is present.
The following does not work because existing cannot be combined with properties:
resource storageAccount 'Microsoft.Storage/storageAccounts#2021-06-01' existing = {
name: storageAccountName
properties: {
networkAcls: {
virtualNetworkRules: [
{
id: subnetId
action: 'Allow'
}
]
}
}
}
Is there any way I can adapt just a tiny bit of an existing resource using Bicep?

UPDATE: I just realized you came from Azure CLI and was trying to find a way in bicep - sorry for not answering your actual question - anyway your post made me think about this in another way other than bicep, so my "answer" is what I came up with...
...sounds like we thought about this in the same manner; using bicep to pimp an existing Storage Account, granting a new subnet access. However I ended up using AzureCLI az storage account network-rule add
e.g.
newSubnet='/subscriptions/<subscr-guid>/resourceGroups/<rg-name-where-vnet-resides>/providers/Microsoft.Network/virtualNetworks/<vnet-name>/subnets/<subnet-name>'
az storage account network-rule add -g <rg-name-where-sa-resides> --account-name <storage-account-name> --subnet $newSubnet
run this from a terminal or put it in an AzureCLI task in a devops pipeline (which is what I needed)

The existing keyword in bicep is used to tell bicep that the resource already exists and you just want a symbolic reference to that resource in the code. If the resource doesn't exist it's likely that the the deployment will fail in some way.
Your first snippet is equivalent to:
resource vnetRule 'Microsoft.Sql/servers/virtualNetworkRules#2021-05-01-preview' = {
name: '${azureSqlServerName}/${azureSqlServerVnetRuleName}'
properties: {
virtualNetworkSubnetId: subnetId
}
}
In your second snippet since you want to update properties you have to provide the complete declaration of the resource, IOW you have to define and deploy the storageAccount. This isn't unique to bicep, it's the way the declarative model in Azure works.
That said, if you want to deploy to another scope in bicep, you can use a module with the scope property. E.g.
module updateStorage 'storage.bicep' = {
scope: resourceGroup(storageResourceGroupName)
name: 'updateStorage'
}
The downside is that you need to make sure you define/declare all the properties need for that storageAccount which is not ideal. There are some ways you can author around that, but if the storageAccount doesn't exist, the deployment is guaranteed to fail. For example, you could assert the storageAccount exists, fetch its properties and then union or modify the properties in the module. You might be able to make that work (depending on the extent of your changes) but it's a bit of an anti-pattern in a declarative model.
That help?

Related

Bicep - create credentials for Managed Identity for use in LinkedService

I am attempting to re-create an Azure Data Factory using Bicep, and specifically i am trying to user a User Assigned Managed Identity for a linked service to an Azure SQL Database.
I am able to create the ua MI by doing the following -
resource uami 'Microsoft.ManagedIdentity/userAssignedIdentities#2018-11-30' = {
name: uamiName
location: location
}
This gets successfully attached to the data factory upon build.
Next i am trying to associate that UA MI to the database connection i am making in the Linked Services section. In the front end its like selected 'User Assigned Managed Identity' and selecting the creds ( or create new). I am trying to do this via Bicep and In order to do this, i first needs credentials - I can't find anywhere in Bicep to create these credentials.
I see lots of old references to Microsoft.DataFactory/factories/credentials - but i can't seem to find that.
Appreciate any help that anyone can offer.
I haven't found much documentation neither.
I've created a credential from data factory studio then export the ARM template. The bicep equivalent looks like that:
param location string
param uamiName string
param dataFactoryName string
// Create the managed identity
resource uami 'Microsoft.ManagedIdentity/userAssignedIdentities#2022-01-31-preview' = {
name: uamiName
location: location
}
// assuming the data factory already exists
resource dataFactory 'Microsoft.DataFactory/factories#2018-06-01' existing = {
name: dataFactoryName
}
// Create the credentials
resource credentials 'Microsoft.DataFactory/factories/credentials#2018-06-01' = {
name: uami.name
parent: dataFactory
properties: {
type: 'ManagedIdentity'
typeProperties: {
resourceId: uami.id
}
}
}

Best bicep structure with CI/CD pipeline

I'm not sure that there is a right answer for this and it will vary per scenario but I am curious because there isn't much documentation that I can find for a code first azure bicep infrastructure. Most examples you find show how to make a resource within a resource group, or using a module to define scope and deploy to another resource group, but what if you're trying to do more?
Let's do the following scenario: using 2 subscriptions(1 for prod, 1 for dev & qa) with 20 resource groups each containing multiple difference resources and you want to manage this within a CI/CD pipeline, plus the 3 environments: prod, qa, and dev. How would you go about this? I can think of a few scenarios but don't necessarily, but nothing sticks out as the best way to do it, maybe I'm missing something.
CI/CD portion:
Let's assume:
az account set --subscription(set our sub)
az group create --name --location (create resource group if it doesn't exist)
az deployment group create --name --resource-group --template-file --parameters(read from our files to deploy to a resource group)
You could pass an array of resource groups to loop through to create the resource group if it doesn't exist.
You could have the resource group list in a parameters file that you read from and do the same thing as above.
You could create a step for every resource group and the resources inside of it.(seems excessive?)
Bicep Portion:
Bicep restrictions: to specify scope(a resource group in our scenario) we'd have to have use modules dealing with multiple resource groups or have a step for each resource group and have a main.bicep file for the different resource groups/resources.
You could create a folder structure for each resource group and the resources inside of it with a main.bicep but that would mean you have a lot of extra deploy steps(seems excessive?).
You could have 1 main.bicep file and have a folder structure that uses a lot of modules to specify your scope while reading the resource group, resource variables etc using an environment parameters.json file.
You could create a folder for each environment, have folders with each environment then create each resource group and resources inside of it not using a parameters.json but using params in each file instead since they would be specific for each environment.
1 final issue:
Lastly let's say you want to add a step before the deployment of resources to use bicep what-if to check what resources will be updated or deleted(this is pretty important!). Last I checked there was an issue where what-if does not work for bicep modules so you wouldn't get the luxury of knowing what changes would be made prior to a deployment with the what-if. That is a pretty big safety net you'd be losing, so would you want to scratch the module strategy all together?
What would be the best way to tackle something like this while keeping it readable for average non experts to be able to hop in and work on it? I would lean towards making a folder structure using modules and reading from an environment parameters.json but I'm not convinced that's the best way, especially if what-if isn't fully working for bicep modules.
IMO this does depend a lot on the scenario, topology, permissions, etc. The way I would start thinking about this is that you want an "environment" that will vary a bit between dev/test and prod. That env has multiple resourceGroups and a dedicated subscription for each env.
In this case, I would use a single bicep "project" (e.g. main.bicep with modules) and change the deployment using parameter files (for dev/test vs. prod). The project would lay down everything needed for the environment (think greenfield). The main.bicep file is a subscription scoped deployment that will create the RGs and all the resources needed. Oversimplified example:
targetScope = 'subscription'
param sqlAdminUsername string'
param keyVaultResourceGroup
param keyVaultName string
param keyVaultSecretName string
param location string = deployment().location
resource kv 'Microsoft.KeyVault/vaults#2021-06-01-preview' existing = {
scope: resourceGroup(subscription().subscriptionId, keyVaultResourceGroup)
name: keyVaultName
}
resource sqlResourceGroup 'Microsoft.Resources/resourceGroups#2021-04-01' = {
name: 'shared-sql'
location: location
}
resource webResourceGroup 'Microsoft.Resources/resourceGroups#2021-04-01' = {
name: 'shared-web'
location: location
}
module sqlDeployment 'modules/shared-sql.bicep' = {
scope: resourceGroup(sqlResourceGroup.name)
name: 'sqlDeployment'
params: {
sqlAdminUsername: sqlAdminUsername
sqlAdminPassword: kv.getSecret(keyVaultSecretName)
location: location
}
}
module webDeployment 'modules/shared-web.bicep' = {
scope: resourceGroup(webResourceGroup.name)
name: 'webDeployment'
params: {
location: location
}
}
A single template + modules that creates the RGs, creates a SQL Server (via module) and an app service plan with an admin website (also via module). You can then parameterize whatever you want to for each environment.
re: what-if - what if will skip evaluation of a module if that module has a parameter that is an output of another module. If you don't pass outputs between modules then the module will be evaluated by what-if. The sample above does not pass outputs - often you don't need to do this because the information output was known by the parent (i.e. main.bicep) but sometimes you can't avoid it - ymmv.
Once you have the template designed in such a way, the pipeline is really straightforward - just deploy the template to the desired subscription.
That help?

How to get Azure App Configuration feature flag value list in bicep template

I would like to get list of already created feature flags from Azure App Configuration in bicep template. I want to pass it to separate bicep file that will use union function on existing and new feature flags to not override already existing ones.
Simillar thing I'm already using for Web App and list() function get existing app settings:
module appConfig './webappsettings.bicep' = {
name: '${deployment().name}-appSettings'
params: {
webAppName: webapp.name
currentAppSettings: list('${webapp.id}/config/appsettings', '2021-03-01').properties
appSettings: allSettings
}
}
How can I achieve something similar for Azure App Configuration to get key values of feature flags?
I tried with below solution but I only got key values of App Configuration
resource configurationStore 'Microsoft.AppConfiguration/configurationStores#2021-10-01-preview' existing = {
name: 'appcfg'
}
module configStoreKeyValues 'inner.bicep' = {
name: 'config-store'
params: {
existingKeyValues: configurationStore.listKeys().value
keyValues: keyValues
contentType: contentType
}
}
using same list() function or listKeys()
list('${configurationStore.id}/keyValues','2021-10-01-preview').properties
I'm getting an error:
Status Message: The resource namespace 'subscriptions' is invalid. (Code:InvalidResourceNamespace)
The "List" operation of key-values is not supported by the control-plane REST API in App Configuration. The listKeys API you used above returns the "Access keys", not the key-value configuration data you are looking for. You can create/update/read individual key-value, feature flag, Key Vault reference as KeyValues resource using Bicep. Feature flag is a special key-value with certain key prefix and content type. Below is an example of feature flag using the ARM template, but it should give you an idea of how to do the same in Bicep.
https://azure.microsoft.com/resources/templates/app-configuration-store-ff/
Note that the "List" operation of key-values is supported in the data-plane REST API of App Configuration. Besides the REST API, it's also accessible via Azure CLI, Azure portal, and App Configuration SDKs programmatically.

Can we refer a storage account properties in second bicep file?

I have 2 azure bicep files.
Storage.bicep
cdn.bicep
Now, I successfully created a storage account and BLOB container from storage.bicep. Is it possible to get the value of storage account properties from storage.bicep to use it in cdn.bicep ?
Yes, you can reference Storage Account in cdn.bicep as existing resource if you pass Storage Account name in to it.
resource stg 'Microsoft.Storage/storageAccounts#2019-06-01' existing = {
name: 'examplestorage'
}
output blobEndpoint string = stg.properties.primaryEndpoints.blob
Just make sure you declare cdn.bicep has dependency on Storage.bicep and is executed after it.
The example above is taken from documentation.

Clone a resource group

So, I've setup a lab environment in a azure resource group with a domain controller, sql-server and a web server. I would like to clone that whole resource group to a new one when needed and by stat I mean with everything intact. Is that possible?
I don't know of anyway to do a direct copy, but you can export a JSON template from the portal or PowerShell that will come close to giving you something you can automate. I say "close" because the feature is still in preview and some resources can't be exported.
In portal.azure.com go to:
Resource Groups > [your rg] > Settings > Export...
In PowerShell see:
Export-AzureRMResourceGroup
So, the solution I came up with was to simply creating a new Resource group, copy the template (not sysprepped) vhds to that new Resource group and creating new VMs with the vhd's attached.
I do wish that there was a "Clone resource group" button somewhere :)
To clone a resource group
1. Go to: Resource groups > [resource group to copy] > Settings (sidebar grouping) > Export template > Deploy
My resource group:
Issues on my way with solutions:
Parameter osDisk.managedDisk.id is not allowed
'Edit Template' button
Remove id field under osDisk.managedDisk
See: osDisk.managedDisk.id’ is not allowed
Disk [disk subscription] cannot be attached as the disk is already owned by VM [VM subscription]
Make the data disk snapshot
Create a new disk based on the snapshot (important: the disk name cannot be changed)
Change proper id parameter value in settings or edit parameters (under 'Edit parameters' button)
Changing property 'dataDisk.name' is not allowed.
As mentioned above. Do not change the disk name when coping the disk.
Required parameter 'adminPassword' is missing (null)."
'Edit Parameters'
Add:
"adminPassword": {
"value": null
}
'Edit Template'
Add:
"adminPassword": {
"defaultValue": null,
"type": "SecureString"
}
Set the adminPassword in settings
Check out Jeff Bow's scripts to copy / clone a resource group:
Copy-AzureRMresourceGroup.ps1
Copies Azure V2 (ARM) resources from one Azure Subscription to
another.
Unlike the Move-AzureRMresource cmdlet, this script allows
you to move between subscriptions in different Tenants and different
Azure Environments. Requires AzureRM module version 6.7 or later.
Clone-AzureRMresourceGroup.ps1
Clones Azure V2 (ARM) resources from one resource group into a new
resource group in the same Azure Subscriptions.
Requires AzureRM module version 6.7 or later.
This is intended mostly for Azure V2 virtual machines and will include
copying virtual disks, virtual network, load balancers, Public IPs and
other associated storage accounts, blob files and now managed disks.
PS suggest you upvote this Azure feedback to provide an Azure-native command, Copy resource groups:
https://feedback.azure.com/forums/281804-azure-resource-manager/suggestions/14142671-copy-resource-groups
While it doesn't look to be specifically designed for this use case, I have successfully moved RGs using Azure Resource Mover, released in 2021. Since it isn't mandatory to remove any resources from the source RG, this has worked better for me than exporting the RG JSON and redeploying, etc.

Resources