Azure App Configuration - Conditionally create feature flag using bicep - azure

I plan to use the Feature Management functionality within Azure App Configuration service.
Using bicep I create the configuration store.
Template: https://learn.microsoft.com/en-us/azure/templates/microsoft.appconfiguration/2021-10-01-preview/configurationstores?tabs=bicep.
Using bicep I add a feature flag.
Template: https://learn.microsoft.com/en-us/azure/templates/microsoft.appconfiguration/2021-10-01-preview/configurationstores/keyvalues?tabs=bicep.
The value should be initially set to false and once available it's up to the business/ops to change the value to true in the Azure Portal.
So I'm looking for a way to conditionally create the feature flag in my bicep; create the feature flag if it not exists. When it already exists, the bicep should skip creation because it could otherwise overwrite/reset the flag value changed by the business/ops.
I found this issue on GitHub from which I conclude that bicep does not yet support this requirement: https://github.com/Azure/bicep/issues/4023
Any suggestions for a workaround?

You can't really do that unless you check manually if the featureFlag exists before running the deployment.
ARM templates (and Bicep) try to be idempotent so if you apply the same template multiple times it will reset any manual changes.
Here is a bicep file that creates a config store and feature flag:
// main.bicep
param location string = resourceGroup().location
param configurationStoreName string
param featureFlagExists bool
param featureFlagName string
// Create the configuration store
resource configurationStore 'Microsoft.AppConfiguration/configurationStores#2021-10-01-preview' = {
name: configurationStoreName
location: location
sku: {
name: 'free'
}
properties: {
disableLocalAuth: false
enablePurgeProtection: false
encryption: {}
softDeleteRetentionInDays: 0
}
}
// Only create the feature flag if not exists
resource featureFlag 'Microsoft.AppConfiguration/configurationStores/keyValues#2021-10-01-preview' = if (!featureFlagExists) {
name: '.appconfig.featureflag~2F${featureFlagName}'
parent: configurationStore
properties: {
contentType: 'application/vnd.microsoft.appconfig.ff+json;charset=utf-8'
tags: {}
value: '{"id": "${featureFlagName}", "description": "", "enabled": false, "conditions": {"client_filters":[]}}'
}
}
And here is a sample powershell script that invoke it:
check if the config store exists
check if the feature flag exists
run the deployment
$resourceGroupName = "<resource group name>"
$configurationStoreName = "<config store name>"
$featureFlagName = "<feature flag name>"
# Check if the app configuration exists
$appConfigExists = (az appconfig list `
--resource-group $resourceGroupName `
--query "[?name=='$configurationStoreName'].id" `
| ConvertFrom-Json).Length -gt 0
# Check if the feature flag exists
$featureFlagExists = $false
if ($appConfigExists) {
$featureFlagExists = (az appconfig kv list `
--name $configurationStoreName `
--query "[?key=='.appconfig.featureflag/$featureFlagName'].key" `
| ConvertFrom-Json).Length -gt 0
}
az deployment group create `
--resource-group $resourceGroupName `
--template-file .\main.bicep `
--parameters `
configurationStoreName=$configurationStoreName `
featureFlagExists=$featureFlagExists `
featureFlagName=$featureFlagName

Related

Unable to create a azure b2c tenant with bicep

I'm trying to create a azure B2c tenant with bicep. When I do it in the portal everything is ok. With terraform also everything is ok. But with bicep I get this
New-AzSubscriptionDeployment : 5:41:21 PM - The deployment 'xpto-devTEST' failed with error(s). Showing 2 out of 2 error(s).
Status Message: The response for resource had empty or invalid content. (Code:ResourceDeploymentFailure)
Status Message: At least one resource deployment operation failed. Please list deployment operations for details. Please see
{
"error": {
"code": "ResourceDeploymentFailure",
"message": "The response for resource had empty or invalid content."
}
} (Code:InternalServerError)
CorrelationId: c0a20039-0a78-44c3-94fb-998c53c661a4
At line:1 char:1
New-AzSubscriptionDeployment -Name xpto-devTEST -Template ...
+ CategoryInfo : NotSpecified: (:) [New-AzDeployment], Exception
+ FullyQualifiedErrorId : >Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.NewAzureSubscriptionDeploymentCmdlet
My main bicep have this
#description('the name of the environment. this must be dev, test, or prod.')
#allowed([
'dev'
'test'
'prod'
])
param environmentname string = 'dev'
#description('the location of the resource')
param location string = 'northeurope'
#description('Name of the resource group')
param resourceGroupName string = 'xpto-${environmentname}'
targetScope = 'subscription'
resource rg 'microsoft.resources/resourcegroups#2021-04-01' = {
name: resourceGroupName
location:location
}
module azureb2c 'modules/azureB2C.bicep' = {
name: 'xptoTenant-${environmentname}'
scope: rg
}
My module have this
#description('The name of the environment. This must be dev, test, or prod.')
#allowed([
'dev'
'test'
'prod'
])
param environmentName string = 'dev'
#description('The location of the resource')
param location string = 'Europe'
resource b2c 'Microsoft.AzureActiveDirectory/b2cDirectories#2019-01-01-preview' ={
name: 'xpto${environmentName}Tenant.onmicrosoft.com'
location: location
sku: {
name: 'PremiumP1'
}
properties:{
createTenantProperties:{
displayName: 'xpto-${environmentName}-tenant'
countryCode: 'PT'
}
}
}
The Command that I'm running is this
New-AzSubscriptionDeployment -Name xpto-devTEST -TemplateFile main.bicep -TemplateParameterFile .\main.parameters-dev.json -DeploymentDebugLogLevel All
Can Anyone help me?
resource b2cDirectory 'Microsoft.AzureActiveDirectory/b2cDirectories#2021-04-01' = {
#disable-next-line no-hardcoded-location
location: 'Australia'
name: '<your-name>.onmicrosoft.com'
sku: {
name: 'PremiumP2'
tier: 'A0'
}
properties: {
createTenantProperties: {
countryCode: 'AU'
displayName: '<Your description>'
}
}
tags: {
Department: 'Dev'
}
}
Works! Probably name is important and you need to name your resource with a valid DNS name.
It is not possible to create from any IAC tools . As Microsoft.AzureActiveDirectory/b2cDirectories#2019-01-01-preview Rest API hasn't been added to the IAC modules.
As an alternative you can use Az CLI along with Rest API's with the below script :
az login
SUBS=`az account show --query "id" -o tsv` \
RG=b2cdemotest \
DOMAIN=b2cdemo1ansuman \
LOCATION=westeurope
az group create --name $RG --location $LOCATION
# deploy
az rest --method put --url https://management.azure.com/subscriptions/$SUBS/resourceGroups/$RG/providers/Microsoft.AzureActiveDirectory/b2cDirectories/$DOMAIN.onmicrosoft.com?api-version=2019-01-01-preview --body #b2c.json --verbose
# get
az account tenant list
az rest --method get --url https://management.azure.com/subscriptions/$SUBS/resourceGroups/$RG/providers/Microsoft.AzureActiveDirectory/b2cDirectories/$DOMAIN.onmicrosoft.com?api-version=2019-01-01-preview | jq
# delete
az rest --method delete --url https://management.azure.com/subscriptions/$SUBS/resourceGroups/$RG/providers/Microsoft.AzureActiveDirectory/b2cDirectories/$DOMAIN.onmicrosoft.com?api-version=2019-01-01-preview --verbose
> verify if B2C tenant was removed then remove the Resource Group
az group delete --name $RG
b2c.json file
{
"location":"europe",
"sku": {
"name":"Standard",
"tier":"A0"
},
"properties": {
"createTenantProperties": {
"displayName":"b2c-demo-ansuman",
"countryCode":"PL"
}
}
}

How to List App Services ( Webapp ) Runtimes?

I have a thousand app services in different subscriptions running on Microsoft Azure. I need to pull all the runtime version for each webapp.
I used this CLI Azure command to list the runtimes actually running in my subscriptions:
az webapp list-runtimes --subscription
But, I need to have the information for each webapp.
How can I do it?
This is more difficult than it should be given that not all properties are exposed by all CLI methods (or REST API calls), and some of the ones that are aren't populated. It is made even more complex by the fact that different properties are used for Windows and Linux hosts.
However it can be done using a combination of CLI commands. This PowerShell script will generate a JSON file for all the app services in a subscription, with their relevant runtimes and versions. Like this:
Script:
# Define a collection to hold the output
$runtimes = [System.Collections.Generic.List[object]]#()
# Get subscription id
Write-Progress "Fetching subscription id"
$sub = (az account show --query id -o tsv)
# Get all resource groups in the subscription
Write-Progress "Searching for resource groups"
$groups = (az group list --query "[].name" -o tsv)
# Set counter for group progress
$groupCounter = 1;
# Loop through each resource group to find all the web apps in it
foreach($group in $groups) {
# Find web apps in the specified group
Write-Progress "Searching for web apps in resource group $group" -PercentComplete (($groupCounter / $groups.Count) * 100)
$apps =(az webapp list -g $group --query "[?kind=='app' || kind=='app,linux'].name" -o tsv)
# Iterate the web apps
foreach($app in $apps) {
# Query the web app for versions
Write-Progress "Querying web app $app"
$appConfig = (az webapp show -n $app -g $group --query "{java:siteConfig.javaversion,netFramework:siteConfig.netFrameworkVersion,php:siteConfig.phpVersion,python:siteConfig.pythonVersion,linux:siteConfig.linuxFxVersion}") | ConvertFrom-Json
# Define an output object
$output = [PSCustomObject]#{
group = $group
name = $app
host = $null
runtime = $null
version = $null
}
# Determine which type of app service it is and get the values accordingly
if($appConfig.linux -eq "") {
# Windows platform
$output.host = "windows"
# Query the app config to get the metadata for the current stack
$uri = "https://management.azure.com/subscriptions/$sub/resourceGroups/$group/providers/Microsoft.Web/sites/$app/config/metadata/list?api-version=2020-10-01"
$output.runtime = (az rest --method post --uri $uri --query "properties.CURRENT_STACK" -o tsv)
# Determine the version of the relevant stack
$output.version = switch($output.runtime) {
"dotnet" {$appConfig.netFramework}
"dotnetcore" {$null}
"python" {$appConfig.python}
"php" {$appConfig.php}
"java" {$appConfig.java}
default {$null}
}
} else {
# Linux platform
$output.host = "linux"
# Split out the stack from the version
$output.runtime = $appConfig.linux.split("|")[0]
$output.version = $appConfig.linux.split("|")[1]
}
$runtimes.Add($output)
}
$groupCounter = $groupCounter + 1
}
# Convert the collection to JSON and write it out to a file
Write-Output $runtimes | ConvertTo-Json > "webapp-runtimes-$sub.json"
Output:
[
{
"group": "sample-group",
"name": "sample-ruby-app",
"host": "linux",
"runtime": "RUBY",
"version": "2.6"
},
{
"group": "php-group",
"name": "sample-php-app",
"host": "windows",
"runtime": "php",
"version": "7.3"
},
{
"group": "linux-apps",
"name": "sample-node-app",
"host": "linux",
"runtime": "NODE",
"version": "14-lts"
},
{
"group": "linux-apps",
"name": "sample-dotnetcore-app",
"host": "linux",
"runtime": "DOTNETCORE",
"version": "3.1"
}
]
Here is an example how to get runtime versions for each app services in multiple subscriptions:
Assuming that you have all the subscriptions within same account
Assuming that you have Windows WebApps
Code:
$subscriptions = az account list | ConvertFrom-Json
foreach ($s in $subscriptions) {
az account set -s $s.name
$webapps = az resource list --resource-type 'Microsoft.Web/sites' | ConvertFrom-Json
foreach ($w in $webapps) {
$config = az webapp config show --name $w.name --resource-group $w.resourcegroup | ConvertFrom-Json
$res = $config.name + ""
$res += $config.windowsFxVersion
$res >> file.txt
}
}

creating AKS cluster with managed identity and associated with an acr by az cli script error

I am new to power-shell scripts and I tried to run below script that will create an AKS-cluster with managed identity also associated with an ACR . But it was giving an error at "managed identity" line..
Param(
[parameter(Mandatory = $false)]
[string]$subscriptionName = "azure-subcription",
[parameter(Mandatory = $false)]
[string]$resourceGroupName = "demoRG",
[parameter(Mandatory = $false)]
[string]$resourceGroupLocaltion = "East US 2",
[parameter(Mandatory = $false)]
[string]$clusterName = "nginxCluster",
[parameter(Mandatory = $false)]
[int16]$workerNodeCount = 3,
[parameter(Mandatory = $false)]
[string]$kubernetesVersion = "1.19.3",
[parameter(Mandatory = $false)]
[string]$acrRegistryName = "ngAcrRegistrydemo"
)
# Set Azure subscription name
Write-Host "Setting Azure subscription to $subscriptionName" -ForegroundColor Yellow
az account set --subscription=$subscriptionName
$aksRgExists = az group exists --name $resourceGroupName
Write-Host "$resourceGroupName exists : $aksRgExists"
if ($aksRgExists -eq $false) {
# Create resource group name
Write-Host "Creating resource group $resourceGroupName in region $resourceGroupLocaltion" -ForegroundColor Yellow
az group create `
--name=$resourceGroupName `
--location=$resourceGroupLocaltion `
--output=jsonc
}
$aks = az aks show `
--name $clusterName `
--resource-group $resourceGroupName `
--query name | ConvertFrom-Json
$aksCLusterExists = $aks.Length -gt 0
if ($aksCLusterExists -eq $false) {
# Create AKS cluster
Write-Host "Creating AKS cluster $clusterName with resource group $resourceGroupName in region $resourceGroupLocaltion" -ForegroundColor Yellow
az aks create `
--resource-group=$resourceGroupName `
--name=$clusterName `
--node-count=$workerNodeCount `
--enable-managed-identity `
--output=jsonc `
--kubernetes-version=$kubernetesVersion `
--aks-custom-headers="CustomizedUbuntu=aks-ubuntu-1804,ContainerRuntime=containerd" `
--attach-acr=$acrRegistryName
}
# Get credentials for newly created cluster
Write-Host "Getting credentials for cluster $clusterName" -ForegroundColor Yellow
az aks get-credentials `
--resource-group=$resourceGroupName `
--name=$clusterName `
--overwrite-existing
Write-Host "Successfully created cluster $clusterName with $workerNodeCount node(s)" -ForegroundColor Green
Write-Host "Creating cluster role binding for Kubernetes dashboard" -ForegroundColor Green
# kubectl create clusterrolebinding kubernetes-dashboard `
# -n kube-system `
# --clusterrole=cluster-admin `
# --serviceaccount=kube-system:kubernetes-dashboard
Error Msg is coming like as "az: error: unrecognized arguments: --enable-managed-identity".
Please help or give suggestions on how to enable managed identity also associated with AKS-clusters.
Many Thanks,
First, there is no parameter --aks-custom-headers of the CLI command az aks create, and the other two-parameter --enable-managed-identity and --attach-acr. You can try it again without the character =, just append the value behind the parameters:
az aks create `
--resource-group $resourceGroupName `
--name $clusterName `
--node-count $workerNodeCount `
--enable-managed-identity `
--kubernetes-version $kubernetesVersion `
--attach-acr $acrRegistryName
You can take a look at the command az aks create. In addition, that's managed identity, not the service principal, so you need to use the command az identity list to get the identity of the AKS in the node group and you can get the node group through CLI command like below:
az aks show -g aksGroup -n aksCluster --query nodeResourceGroup
I updated Azure CLI (version 2.15.1 or later) by using below
https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=azure-powershell
and executed aks creation ps-script as above and it working perfectly .
AKS-infrastructure are created .
Many Thanks..

Unable to deploy Azure ARM with parameters using az cli from powershell script

Trying simple deployment with parameters from a PS script:
$prefix = "xxx"
$location = "switzerlandnorth"
az deployment group create `
--name $timestamp `
--resource-group $resourceGroupName `
--mode incremental `
--verbose `
--template-file .\src\ops\scripts\arm.json `
--parameters "{ `"location`": { `"value`": `"$location`" },`"projectPrefix`": { `"value`": `"$prefix`" } }"
Response with error:
Unable to parse parameter: { location: { value: switzerlandnorth }, projectPrefix: { value: xxx } }
Running from a PS1 script.
As we can see in the error that it is unable to parse the parameters. The correct way to pass the parameters to the az deployment group create command is:
az deployment group create `
--name $timestamp `
--resource-group $resourceGroupName `
--mode "incremental" `
--verbose `
--template-file ".\src\ops\scripts\arm.json" `
--parameters '{ \"location\": { \"value\": \"switzerlandnorth\" },\"projectPrefix\": { \"value\": \"xxx\" } }'
Update:
If you want to pass the PowerShell variables in the Parameters you can do something like below -
$location = "switzerlandnorth"
$projectPrefix = "xxx"
$params = '{ \"location\": { \"value\": \" ' + $location + '\" },\"projectPrefix\": { \"value\": \"' + $projectprefix + '\" } }'
az deployment group create `
--name $timestamp `
--resource-group $resourceGroupName `
--mode "incremental" `
--verbose `
--template-file ".\src\ops\scripts\arm.json" `
--parameters $params
Hope this helps!
Another way you can think of this is using objects/hashtables in PS and then converting to JSON, e.g.
$param = #{
location = "switzerlandnorth"
prefix = "foo"
}
$paramJSON = ($param | ConvertTo-Json -Depth 30 -Compress).Replace('"', '\"') # escape quotes in order to pass the command via pwsh.exe
az deployment group create -g $resourceGroupName--template-file "azuredeploy.json" -p $paramJson
This is a similar example:
https://gist.github.com/bmoore-msft/d578c815f319af7eb20d9d97df9bf657
I'll throw in my two cents here because previous answers are slim on what we are actually doing here. In short the az deployment group create command wants the --parameters argument to be a double serialized JSON string {scoff}. In order to create a value that can be handled by az deployment group create we would need to do something like this:
$stupidlyFormattedParam = #{
someAppSetting1 = #{ value = "setting 1" }
someAppSetting2 = #{ value = "setting 2" }
} | ConvertTo-Json -Compress -Depth 10 | ConvertTo-Json
az deployment group create --name someName--resource-group someGroup --parameters $stupidlyFormattedParam --template-file .\someFile.json

How to update an Azure Cloud Service setting using Azure Powershell

Is it possible to update the value of a setting in an Azure Cloud Service with Azure Powershell?
So far there is no way to update just a single setting (the Service Management API does not allow it - it only accepts the whole service configuration). So, in order to update a single setting, you will have to update the entire configuration. And you can do this with PowerShell:
# Add the Azure Account first - this will create a login promppt
Add-AzureAccount
# when you have more then one subscription - you have explicitly select the one
# which holds your cloud service you want to update
Select-AzureSubscription "<Subscription name with spaces goes here>"
# then Update the configuration for the cloud service
Set-AzureDeployment -Config -ServiceName "<cloud_service_name_goes_here>" `
-Configuration "D:/tmp/cloud/ServiceConfiguration.Cloud.cscfg" `
-Slot "Production"
For the the `-Configuration' parameter I have provided full local path to the new config file I want to use with my cloud service.
This is verified and working solution.
As astaykov says, you can't update a single cloud config value using Powershell.
But you can read all of the settings, update the one you wish to change, save it to a temp file, and then set all the settings again, like so:
UpdateCloudConfig.ps1:
param
(
[string] $cloudService,
[string] $publishSettings,
[string] $subscription,
[string] $role,
[string] $setting,
[string] $value
)
# param checking code removed for brevity
Import-AzurePublishSettingsFile $publishSettings -ErrorAction Stop | Out-Null
function SaveNewSettingInXmlFile($cloudService, [xml]$configuration, $setting, [string]$value)
{
# get the <Role name="Customer.Api"> or <Role name="Customer.NewsletterSubscription.Api"> or <Role name="Identity.Web"> element
$roleElement = $configuration.ServiceConfiguration.Role | ? { $_.name -eq $role }
if (-not($roleElement))
{
Throw "Could not find role $role in existing cloud config"
}
# get the existing AzureServiceBusQueueConfig.ConnectionString element
$settingElement = $roleElement.ConfigurationSettings.Setting | ? { $_.name -eq $setting }
if (-not($settingElement))
{
Throw "Could not find existing element in cloud config with name $setting"
}
if ($settingElement.value -eq $value)
{
Write-Host "No change detected, so will not update cloud config"
return $null
}
# update the value
$settingElement.value = $value
# write configuration out to a file
$filename = $cloudService + ".cscfg"
$configuration.Save("$pwd\$filename")
return $filename
}
Write-Host "Updating setting for $cloudService" -ForegroundColor Green
Select-AzureSubscription -SubscriptionName $subscription -ErrorAction Stop
# get the current settings from Azure
$deployment = Get-AzureDeployment $cloudService -ErrorAction Stop
# save settings with new value to a .cscfg file
$filename = SaveNewSettingInXmlFile $cloudService $deployment.Configuration $setting $value
if (-not($filename)) # there was no change to the cloud config so we can exit nicely
{
return
}
# change the settings in Azure
Set-AzureDeployment -Config -ServiceName $cloudService -Configuration "$pwd\$filename" -Slot Production
# clean up - delete .cscfg file
Remove-Item ("$pwd\$filename")
Write-Host "done"

Resources