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"
}
}
}
Related
I am trying to create an app registration in Azure AD B2C. I need to add a scope using Azure CLI (which can be added by going to Expose a API blade in portal)
I can retrieve my app using
az rest --method GET --uri https://graph.microsoft.com/v1.0/applications/id
But when I run the following command
az ad app update --id 'id' --set api.oauth2PermissionScopes=#scopes.json
I get this error
Couldn't find 'api' in ''. Available options: []
Here is the scopes.json file
{
"api": {
"oauth2PermissionScopes": [
{
"type": "User",
"isEnabled": true,
"adminConsentDisplayName": "deafult",
"adminConsentDescription": "deafult",
"id": "73a43c0e-9a5e-4646-9d1e-c56a43279f99",
"value": "deafult",
"userConsentDisplayName": "deafult",
"userConsentDescription": "deafult"
}
]
}
}
Any suggestions would be much appreciated
I tried in my environment and got below results:
The latest Azure CLI command "az ad app update" does not include the oauth2permissions anymore.
I tried with below scripts and added scope successfully.
$AppId ="your application id"
$scopeGUID = [guid]::NewGuid()
$permission = #{
adminConsentDescription="admin only"
adminConsentDisplayName="readwrite"
id="$scopeGUID"
isEnabled=$true
type="User"
userConsentDescription="null"
userConsentDisplayName="null"
value="readwrite"
}
$azAppObjId = (az ad app show --id $AppId | ConvertFrom-JSON).id
$accesstoken = (Get-AzAccessToken -Resource "https://graph.microsoft.com/").Token
$header = #{
'Content-Type' = 'application/json'
'Authorization' = 'Bearer ' + $accesstoken
}
$bodyaccess = #{
'api' = #{
'oauth2PermissionScopes' = #($permission)
}
}|ConvertTo-Json -d 3
Invoke-RestMethod -Method Patch -Headers $header -Uri "https://graph.microsoft.com/v1.0/applications/$azAppObjId" -Body $bodyaccess
Powershell:
Portal:
Also checked with commands:
az ad app show --id < application id >
Output:
Reference:
Please refer the similar kind of SO-Thread which is resolved by A2AdminGuy.
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
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
}
}
Using the Azure CLI 2.x, I cannot find a way to "add a Scope" under the expose an API section in Azure AD Portal.
What I do see is if I pass the --identifier-uris when the app is created, the APP ID URI and a Scope get automatically set:
`az ad app create --display-name "$appName" --identifier-uris "https://$tenantDomain/$appName" --reply-urls "$replyUrl" --oauth2-allow-implicit-flow true`
Not what I expected nor what I want
So, I removed --identifier-urls from the create command and added the scope I wanted in manually. then I see via manifest what I'm looking for under OAuth2Permissions as shown below. Can I put this in manifest.json with a new guid and insert it somehow?
What CLI command supports the explicit support to define a Scope?
Then Adding a Client application I would need to select the defined Scope, how is this referenced?
Documentation is very sparse, IMO. This reference is very helpful but nothing in here talks about adding scopes and clients. https://learn.microsoft.com/en-us/cli/azure/ad?view=azure-cli-latest. Any help towards samples or documentation much appreciated.
As of 7/29/22 the latest Azure CLI command "az ad app update" does not include the oauth2permissions anymore. If you try the above, you will bang you head and hopefully find this post. The new location of these permissions on the app regs are located at api.oauth2PermissionScopes as an array.
In order to get around this I used a combination of a few items from this post and had to get creative as the Azure docs are also still incorrect.
It still stands true that if you have an existing API exposed, you have to disable it to modify the scopes. If you have a fresh app registration you can apply this direct without issue. Hopefully this helps someone like me that has automations that are now broken due to the changes to how apps are registered and the manifest changes. If you don't know about changes to app registrations, I recommend you review. If you got this far though, I assume you already did.
# Add API Read Scope:
$scopeGUID = [guid]::NewGuid()
$scopeJSONHash = #{
adminConsentDescription="$apiName on $svrAppRegName"
adminConsentDisplayName="$apiName on $svrAppRegName"
id="$scopeGUID"
isEnabled=$true
type="User"
userConsentDescription="$apiName on $svrAppRegName"
userConsentDisplayName="$apiName on $svrAppRegName"
value="$apiName"
}
$azAppOID = (az ad app show --id $serverApplicationId | ConvertFrom-JSON).id
$accesstoken = (Get-AzAccessToken -Resource "https://graph.microsoft.com/").Token
$header = #{
'Content-Type' = 'application/json'
'Authorization' = 'Bearer ' + $accesstoken
}
$bodyAPIAccess = #{
'api' = #{
'oauth2PermissionScopes' = #($scopeJSONHash)
}
}|ConvertTo-Json -d 3
#You can try az rest, I used Invoke-RestMethod though.
#$graphURL="https://graph.microsoft.com/v1.0/applications/$azAppOID"
#az rest --method PATCH --uri $graphurl --headers $header --body $bodyAPIAccess
Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/v1.0/applications/$azAppOID" -Headers $header -Body $bodyAPIAccess
From this article Azure CLI: Create an Azure AD application for an API that exposes OAuth2 Permissions
You can use the az ad app update command (see documentation)
You can then set an application’s property by using the optional parameter –set
Create a oauth2-permissions.json containing the permission:
[
{
"adminConsentDescription": "Access CP Debug Desc",
"adminConsentDisplayName": "Access CP Debug",
"id": "85b8f1a0-0733-47dd-9af4-cb7221dbcb73",
"isEnabled": true,
"type": "Admin",
"userConsentDescription": null,
"userConsentDisplayName": null,
"value": "Access"
}
]
Run this script, it will create the app, disable the existing scope and add the new scope:
# Create the app registration
APP_REG=$(az ad app create --display-name myapi --identifier-uris https://myapi)
# Get the app id
APP_ID=$(echo $APP_REG | jq -r '.appId')
# disable default exposed scope
DEFAULT_SCOPE=$(az ad app show --id $APP_ID | jq '.oauth2Permissions[0].isEnabled = false' | jq -r '.oauth2Permissions')
az ad app update --id $APP_ID --set oauth2Permissions="$DEFAULT_SCOPE"
# Create new scopes from file 'oath2-permissions'
az ad app update --id $APP_ID --set oauth2Permissions=#oauth2-permissions.json
With help from the thread above, and a ton of trial-n-error plus a pretty useful link, I was able to work out the CLI script to add scope using a windows environment. PowerShell is not happy with 'jq' on windows and use of the back-tick had to be removed to get things working. Now I need to solve adding preAuthorizedApplication with the CLI.
$userAccessScopeApi = '{
"lang": null,
"origin": "Application",
"adminConsentDescription": "Access CP Debug desc",
"adminConsentDisplayName": "Access CP Debug",
"id": "--- replaced in scripts ---",
"isEnabled": true,
"type": "Admin",
"userConsentDescription": null,
"userConsentDisplayName": null,
"value": "Access"
}' | ConvertTo-Json | ConvertFrom-Json
`
Write-Host " - 1 read oauth2permissions"
#(az ad app show --id $appid)
$appjson = (az ad app list --display-name $appName)
$app = $appjson | ConvertFrom-Json
$oauth2Permissions = $app.oauth2Permissions
$oauth2Permissions[0].isEnabled = 'false'
$oauth2Permissionsjson = ConvertTo-Json -InputObject #($oauth2Permissions)
Write-Host " - 2 disable oauth2Permission in Azure App Registration"
$oauth2Permissionsjson | Out-File -FilePath .\oauth2Permissionsold.json
az ad app update --id $appId --set oauth2Permissions=#oauth2Permissionsold.json
Write-Host " - 3 delete the default oauth2Permission"
az ad app update --id $appId --set oauth2Permissions='[]'
Write-Host " - 4 add the new scope required add the new oauth2Permissions values"
$oauth2PermissionsApiNew = $userAccessScopeApi | ConvertFrom-Json
$oauth2PermissionsApiNew[0].id = New-Guid
$oauth2PermissionsApiNew = ConvertTo-Json -InputObject #($oauth2PermissionsApiNew)
# Write-Host "new oauth2permissions : " + $oauth2PermissionsApiNew"
$oauth2PermissionsApiNew | Out-File -FilePath .\oauth2Permissionsnew.json
az ad app update --id $appId --set oauth2Permissions=#oauth2Permissionsnew.json
Write-Host " - Updated scopes (oauth2Permissions) for App Registration: $appId"`
As A2AdminGuy mentioned, it is not possible to update the oauth2Permissions direct anymore, but you can still use az ad app update.
Execute the command az ad app show --id $appClientId and you will see that the oauth2PermissionScopes is inside the api now:
"api": {
"acceptMappedClaims": null,
"knownClientApplications": [],
"oauth2PermissionScopes": [],
"preAuthorizedApplications": [],
"requestedAccessTokenVersion": null
}
You need to update the api property to be able to set the oauth2PermissionScopes:
$apiScopeId = [guid]::NewGuid().Guid
$apiScopeJson = #{
requestedAccessTokenVersion = 2
oauth2PermissionScopes = #(
#{
adminConsentDescription = "$AppName on $EnvironmentAbbrev"
adminConsentDisplayName = "$AppName on $EnvironmentAbbrev"
id = "$apiScopeId"
isEnabled = $true
type = "User"
userConsentDescription = "$AppName on $EnvironmentAbbrev"
userConsentDisplayName = "$AppName on $EnvironmentAbbrev"
value = "authenticate"
}
)
} | ConvertTo-Json -d 4 -Compress
$apiUpdateBody = $apiScopeJson | ConvertTo-Json -d 4
az ad app update --id $apiClientId --set api=$apiUpdateBody --verbose
The --verbose parameter can be removed, but interestingly, the update command is a PATH request to the graph API.
Since it is a PATH request, you don't always need to send the whole api property, you can do two requests to update different properties and the previous one will not be affected.
This is how you can update a SPA redirectUris:
$appSpaJson = #{
redirectUris = #("http://localhost:3000")
} | ConvertTo-Json -d 3 -Compress
$appUpdateBody = $appSpaJson | ConvertTo-Json -d 4
az ad app update --id $appClientId --set spa=$appUpdateBody
Here's the approach I ended up with in bash on WSL Ubuntu, in the event it's useful for anyone else:
# replace all <value> blocks with your own value
# create app registration and extract appId
clientid=$(az ad app create \
--display-name <name> \
--query appId \
--output tsv)
# generate a UUID for the scope
uuid=$(uuidgen)
# set the API object as a JSON object
api=$(echo '{
"acceptMappedClaims": null,
"knownClientApplications": [],
"oauth2PermissionScopes": [{
"adminConsentDescription": "<description>",
"adminConsentDisplayName": "<name>",
"id": "'$uuid'",
"isEnabled": true,
"type": "User",
"userConsentDescription": "<description>",
"userConsentDisplayName": "<name>",
"value": "<value>"
}],
"preAuthorizedApplications": [],
"requestedAccessTokenVersion": 2
}' | jq .)
# Update app registration with App ID URL and api object
az ad app update \
--id $clientid \
--identifier-uris api://$clientid \
--set api="$api"
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