ARM Template complex parameters from Runbook Workflow - azure

Have a question about ARM template deployment, specifically calling that deployment from Runbook Powershell workflow using New-AzureRmResourceGroupDeployment cmdlet.
I am trying to use dynamic copy loop in in doing so I am using following formatted parameter in the template:
"aseApAppSettings": {
"type": "object",
"defaultValue": {
"apps": [
{
"name": "app-api-ecom",
"kind": "api"
},
{
"name": "app-ecom",
"kind": "web"
}
]
}
},
Then I create resources based on that:
{
"type": "Microsoft.Web/sites",
"kind": "[parameters('aseApAppSettings').apps[copyIndex()].kind]",
"name": "[concat(parameters('aseApName'),'sv-',parameters('aseApAppSettings').apps[copyIndex()].name)]",
"apiVersion": "2016-08-01",
"location": "East US 2",
"scale": null,
"properties": {...
},
"copy": {
"name": "svLoop",
"count": "[length(parameters('aseApAppSettings').apps)]"
},
"dependsOn": []
},
All works when template is deployed through Template Deployment
I need to call for this deployment from Powershell Workflow runbook and having tough time defining the parameter
I've tried setting it as
{"apps":[{"name":"falcon-api-ecom","kind":"api"},{"name":"falcon-ecom","kind":"web"}]}
during test but it fails with message "Cannot find parameter"
So I have tried using ConvertFrom-Json
But it sends this to my template
"CliXml": "<Objs Version=\"1.1.0.1\"
xmlns=\"http://schemas.microsoft.com/powers...
Please help,
Thanks
Sample from Runbook
workflow Build-Ase {
param
(
#Environment Parameters
[Parameter(Mandatory = $true)]
[object]
$aseApAppSettings
)
$params = #{
"aseApAppSettings" = $aseApAppSettings;
}
$job = New-AzureRmResourceGroupDeployment -ResourceGroupName $vnetRGName -TemplateUri $templateParameterUri -TemplateParameterObject $params
Write-Output $job

Nested objects didn't work for me either, but passing them in as a json string combined with the json function did work for me
Deployment script
$addionalParameters = New-Object -TypeName Hashtable
$addionalParameter1 = "{ ""prop1"": [ { ""name"": ""a"", ""value"": ""1"" }, { ""name"": ""b"", ""value"": ""2"" } ], ""prop2"": { ""name"": ""c"", ""value"": ""3"" } }"
$addionalParameters["myComplexNestedOnjectAsJsonString"] = $addionalParameter1
$deploymentOutput = New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName -TemplateFile $templateFilePath `
-TemplateParameterFile $parametersFilePath #addionalParameters
Template
{
"parameters": {
"myComplexNestedObjectAsJsonString": {
"type": "string"
}
},
"variables": {
"myComplexNestedObject" : "[json(parameters('myComplexNestedObjectAsJsonString'))]"
},
"resources": [],
"outputs": {
"prop1A": {
"type": "string",
"value": "[variables('myComplexNestedObject').prop1[0].value]"
},
"prop2": {
"type": "string",
"value": "[variables('myComplexNestedObject').prop2.value]"
}
}
}

Try using splatting. For me its the only thing that works with complex nested parameter objects. Also note how the aseApAppSettings parameter is constructed.
$params = #{
$aseApAppSettings = #{ #( {name=...;kind=...},{...},...,{...} ) }
}
New-AzureRmResourceGroupDeployment -ResourceGroupName $vnetRGName -TemplateUri $templateParameterUri #params
ps. ... represent placeholders

Related

Unable to connect the API connection to the logic App via ARM template in terraform

In my terraform I have created a logic app and its workflow with the help of a ARM Template. The 2 connections used in the logic app is also created via ARM template. But somehow even though the resources get created in AZURE. But when I got to the logic app, I always have to manually update the connection in the workflow. How can we make it automatic.
//First connection
resource "azurerm_template_deployment" "exampleeventhub" {
name = "acctesttemplate-44"
resource_group_name = Resourcegrpname
template_body = <<DEPLOY
{
"$schema": https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#,
"contentVersion": "1.0.0.0",
"parameters": {
"connections_eventhubs_name": {
"defaultValue": "eventhubs",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "[parameters('connections_eventhubs_name')]",
"location": "qwerty",
"kind": "V1",
"properties": {
"displayName": "eventhubconnection",
"statuses": [
{
"status": "Connected"
}
],
"customParameterValues": {},
"nonSecretParameterValues": {},
"createdTime": "aaaaa",
"changedTime": "bbbb",
"api": {
"name": "[parameters('connections_eventhubs_name')]",
"displayName": "Event Hubs",
"description": "Connect to Azure Event Hubs to send and receive events.",
"iconUri": "[concat('https://connectoricons-prod.azureedge.net/releases/v1.0.1480/1.0.1480.2454/', parameters('connections_eventhubs_name'), '/icon.png')]",
"brandColor": "#c4d5ff",
"id": "[concat('/subscriptions/1111/providers/Microsoft.Web/locations/qwerty/managedApis/', parameters('connections_eventhubs_name'))]",
"type": "Microsoft.Web/locations/managedApis"
},
"testLinks": []
}
}
]
}
DEPLOY
deployment_mode = "Incremental"
}
//Second connection
resource "azurerm_template_deployment" "exampledatacollector" {
name = "acctesttemplate-45"
resource_group_name = Resourcegrpname
template_body = <<DEPLOY
{
"$schema": https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#,
"contentVersion": "1.0.0.0",
"parameters": {
"connections_thengadatacollector_name": {
"defaultValue": "thengadatacollector",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "[parameters('connections_thengadatacollector_name')]",
"location": "qwerty",
"kind": "V1",
"properties": {
"displayName": "azuredatacollector",
"statuses": [
{
"status": "Connected"
}
],
"customParameterValues": {},
"nonSecretParameterValues": {
"username": "764a2b1e-431d-4e90-87b1-ea6a34dac48f"
},
"createdTime": "aaaa",
"changedTime": "bbbb",
"api": {
"name": "[parameters('connections_thengadatacollector_name')]",
"displayName": "Azure Log Analytics Data Collector",
"description": "Azure Log Analytics Data Collector will send data to any Azure Log Analytics workspace.",
"iconUri": "[concat('https://connectoricons-prod.azureedge.net/releases/v1.0.1480/1.0.1480.2454/', parameters('connections_thengadatacollector_name'), '/icon.png')]",
"brandColor": "#0072C6",
"id": "[concat('/subscriptions/1111/providers/Microsoft.Web/locations/qwerty/managedApis/', parameters('connections_thengadatacollector_name'))]",
"type": "Microsoft.Web/locations/managedApis"
},
"testLinks": []
}
}
]
}
DEPLOY
deployment_mode = "Incremental"
}
//Logic App
resource "azurerm_template_deployment" "example" {
name = "acctesttemplate-46"
resource_group_name = Resourcegrpname
template_body = <<DEPLOY
{
"$schema": https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#,
"contentVersion": "1.0.0.0",
"parameters": {
"workflows_logicapp_name": {
"defaultValue": "logicapp",
"type": "String"
},
"connections_thengadatacollector_externalid": {
"defaultValue": "/subscriptions/1111/resourceGroups/Resourcegrpname/providers/Microsoft.Web/connections/azureloganalyticsdatacollector",
"type": "String"
},
"connections_eventhubs_externalid": {
"defaultValue": "/subscriptions/1111/resourceGroups/Resourcegrpname/providers/Microsoft.Web/connections/eventhubs",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Logic/workflows",
"apiVersion": "2017-07-01",
"name": "[parameters('workflows_logicapp_name')]",
"location": "qwerty",
"properties": {
"state": "Enabled",
"definition": {
"$schema": https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#,
"contentVersion": "1.0.0.0",
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
}
},
"triggers": {
"When_events_are_available_in_Event_Hub": {
"recurrence": {
"frequency": "Minute",
"interval": 3
},
"splitOn": "#triggerBody()",
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "#parameters('$connections')['eventhubs']['connectionId']"
}
},
"method": "get",
"path": "/#{encodeURIComponent('thengaeventhub')}/events/batch/head",
"queries": {
"contentType": "application/octet-stream",
"maximumEventsCount": 50
}
}
}
},
"actions": {
"Send_Data_2": {
"runAfter": {},
"type": "ApiConnection",
"inputs": {
"body": "#base64ToString(triggerBody()?['ContentData'])",
"headers": {
"Log-Type": "testcustimlog"
},
"host": {
"connection": {
"name": "#parameters('$connections')['thengadatacollector_1']['connectionId']"
}
},
"method": "post",
"path": "/api/logs"
}
}
}
},
"parameters": {
"$connections": {
"value": {
"thengadatacollector_1": {
"connectionId": "[parameters('connections_thengadatacollector_externalid')]",
"connectionName": "thengadatacollector",
"id": "/subscriptions/1111/providers/Microsoft.Web/locations/qwerty/managedApis/thengadatacollector"
},
"eventhubs": {
"connectionId": "[parameters('connections_eventhubs_externalid')]",
"connectionName": "eventhubs",
"id": "/subscriptions/1111/providers/Microsoft.Web/locations/qwerty/managedApis/eventhubs"
}
}
}
}
}
}
]
}
DEPLOY
deployment_mode = "Incremental"
}
It is an expected behaviour , if you deploy the ARM Template, your both API Connections will have been created but inside logic apps you will have to update manually the connection by entering your credentials for the service. This is because for finalizing the API connection you need to give the consent but which is not possible in ARM template.
But if you need to finalize the API Connection creation without opening every Logic Apps then you can use PowerShell script .This script will retrieve a consent link for a connection for an OAuth Logic Apps connector. It will then open the consent link and complete authorization to enable a connection.
Param(
[string] $ResourceGroupName = 'YourRG',
[string] $ResourceLocation = 'eastus | westus | etc.',
[string] $api = 'office365 | dropbox | dynamicscrmonline | etc.',
[string] $ConnectionName = 'YourConnectionName',
[string] $subscriptionId = '80d4fe69-xxxx-xxxx-a938-9250f1c8ab03',
[bool] $createConnection = $true
)
#region mini window, made by Scripting Guy Blog
Function Show-OAuthWindow {
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object -TypeName System.Windows.Forms.Form -Property #{Width=600;Height=800}
$web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property #{Width=580;Height=780;Url=($url -f ($Scope -join "%20")) }
$DocComp = {
$Global:uri = $web.Url.AbsoluteUri
if ($Global:Uri -match "error=[^&]*|code=[^&]*") {$form.Close() }
}
$web.ScriptErrorsSuppressed = $true
$web.Add_DocumentCompleted($DocComp)
$form.Controls.Add($web)
$form.Add_Shown({$form.Activate()})
$form.ShowDialog() | Out-Null
}
#endregion
#login to get an access code
Login-AzureRmAccount
#select the subscription
$subscription = Select-AzureRmSubscription -SubscriptionId $subscriptionId
#if the connection wasn't alrady created via a deployment
if($createConnection)
{
$connection = New-AzureRmResource -Properties #{"api" = #{"id" = "subscriptions/" + $subscriptionId + "/providers/Microsoft.Web/locations/" + $ResourceLocation + "/managedApis/" + $api}; "displayName" = $ConnectionName; } -ResourceName $ConnectionName -ResourceType "Microsoft.Web/connections" -ResourceGroupName $ResourceGroupName -Location $ResourceLocation -Force
}
#else (meaning the conneciton was created via a deployment) - get the connection
else{
$connection = Get-AzureRmResource -ResourceType "Microsoft.Web/connections" -ResourceGroupName $ResourceGroupName -ResourceName $ConnectionName
}
Write-Host "connection status: " $connection.Properties.Statuses[0]
$parameters = #{
"parameters" = ,#{
"parameterName"= "token";
"redirectUrl"= "https://ema1.exp.azure.com/ema/default/authredirect"
}
}
#get the links needed for consent
$consentResponse = Invoke-AzureRmResourceAction -Action "listConsentLinks" -ResourceId $connection.ResourceId -Parameters $parameters -Force
$url = $consentResponse.Value.Link
#prompt user to login and grab the code after auth
Show-OAuthWindow -URL $url
$regex = '(code=)(.*)$'
$code = ($uri | Select-string -pattern $regex).Matches[0].Groups[2].Value
Write-output "Received an accessCode: $code"
if (-Not [string]::IsNullOrEmpty($code)) {
$parameters = #{ }
$parameters.Add("code", $code)
# NOTE: errors ignored as this appears to error due to a null response
#confirm the consent code
Invoke-AzureRmResourceAction -Action "confirmConsentCode" -ResourceId $connection.ResourceId -Parameters $parameters -Force -ErrorAction Ignore
}
#retrieve the connection
$connection = Get-AzureRmResource -ResourceType "Microsoft.Web/connections" -ResourceGroupName $ResourceGroupName -ResourceName $ConnectionName
Write-Host "connection status now: " $connection.Properties.Statuses[0]
Reference:
Deploy Logic Apps & API Connection with ARM · in my room (bruttin.com)

Is it possible to read JSON from a file in ARM templates?

Lets say I want to deploy a Logic App using an ARM template. Here is a part of my azuredeploy.json:
...
parameters: {
"logic-app-definition": {
"type": "string",
"metadata": {
"description": "The JSON definition of the logic app."
}
}
},
resources: [
{
"apiVersion": "2016-06-01",
"name": "lapp-my-sample",
"type": "Microsoft.Logic/workflows",
"location": "[resourceGroup().location]",
"properties": {
"definition": "[json(parameters('logic-app-definition'))]",
"state": "Enabled"
}
]
As you can see, the actual JSON definition of the Logic App will be taken from a string-paraemter. This is pretty uncomfortable because the template-JSON is mostly a one-line-JSON-mess mostly.
I wonder if there is a function to read the string-value from a file instead.
There's no way to readFromUri() or anything that direct but you don't need to keep your source in a string or single-line JSON file. Depends a bit on your orchestration (e.g. how you deploy) you can do some manipulation there and pass in a parameter or pull from a "config store" (appConfigStore, keyVault).
Happy to explore some options if you want to...
After #bmoore-msft's answer I decided to share my imprefect solution with the community.
First a simplified version of my deploy.ps1 for brevity:
# the initial stuff like parameters and connectivity etc.
# read file content and perform regex
$ParameterFileContent = Get-Content $TemplateParametersFile
$fileName = [regex]::Matches($ParameterFileContent, "getFileJson\('(.*?)'").captures.groups[1].value
$jsonContent = (Get-Content $fileName -Raw).Replace("`n","").Replace(" ", "")
$jsonContent = [regex]::Matches($jsonContent, '{"definition":(.*)}{1}$').captures.groups[1].value
$jsonContent = $jsonContent.Replace('"', '\"')
$result = [regex]::Replace($ParameterFileContent, "[\[]getFileJson\('(.*?)'\)[\]]", $jsonContent)
Set-Content $TemplateParametersFile -Value $result
# perform deployment
New-AzResourceGroupDeployment -Name ((Get-ChildItem $TemplateFile).BaseName + '-' + ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmm')) `
-ResourceGroupName $ResourceGroupName `
-TemplateFile $TemplateFile `
-TemplateParameterFile $TemplateParametersFile `
-Force -Verbose `
# reset the template file to the original version
Set-Content $TemplateParametersFile -Value $ParameterFileContent
here then is my template parameter file:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
...
"logic-app-definition": {
"value": "[getFileJson('logicApp.json')]"
}
}
}
And finally here is my json template file for the logic app (logicapp.json):
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {
"Condition": {
"actions": {},
"expression": {
"and": [
{ "equals": ["#outputs('HTTP')['statusCode']", 200] }
]
},
"runAfter": { "HTTP": ["Succeeded"] },
"type": "If"
},
"HTTP": {
"inputs": {
"headers": { "Accept": "application/json" },
"method": "POST",
"uri": "https://myuri/"
},
"runAfter": {},
"type": "Http"
}
},
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"triggers": {
"Recurrence": {
"recurrence": { "frequency": "Hour", "interval": 6 },
"type": "Recurrence"
}
}
},
"parameters": {}
}
So I basically create a new magic "function" getFileJson and then I use RegEx before I deploy this thing.
It sounds like what you are trying to do can be done using linked templates:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/linked-templates#linked-template
This way you can keep the Logic App template in a separate file. You can still use parameters to deploy.

How to reference Azure policy set parameters in policy rule correctly?

I have following policy rule. Finally it should check existence of a resourcegroup on subscription level. If it does not exist, a remediation task deployment should be started. I would like to pass parameters to this rule.
{
"if": {
"field": "type",
"equals": "Microsoft.Resources/subscriptions"
},
"then": {
"effect": "DeployIfNotExists",
"details": {
"type": "Microsoft.Resources/subscriptions/resourceGroups",
"name": "my_resource_group",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/{builtinroleGUID}"
],
"existenceScope": "Subscription",
"existenceCondition": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Resources/subscriptions/resourceGroups"
},
{
"field": "name",
"equals": "parameters('resourceGroup')"
}
]
},
"deploymentScope": "Subscription",
"deployment": {
"location": "westeurope",
"properties": {
"mode": "incremental",
"parameters": {
"targetResourceGroup": {
"value": "[parameters('resourceGroup')]"
},
"ascWorkflowName": {
"value": "[parameters('securityAutomationWorkflowName')]"
},
"location": {
"value": "[parameters('location')]"
},
"logicAppName": {
"value": "[parameters('logicAppName')]"
},
"logicAppSubscription": {
"value": "[parameters('logicAppSubscription')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"targetResourceGroup": {
"type": "string"
},
"ascWorkflowName": {
"type": "string"
},
"location": {
"type": "string",
"defaultValue": "westeurope"
},
"logicAppName": {
"type": "string"
},
"logicAppSubscription": {
"type": "string"
}
},
"variables": {
"logicAppName": "[parameters('logicAppName')]",
"logicAppTriggerName": "When_an_Azure_Security_Center_Recommendation_is_created_or_triggered",
"logicAppResourceId": "[concat('/subscriptions/', parameters('logicAppSubscription'), '/resourceGroups/', parameters('targetResourceGroup') , '/providers/Microsoft.Logic/workflows/', variables('logicAppName'))]",
"ascWorkflowTriggerId": "[concat('/subscriptions/', parameters('logicAppSubscription'), '/resourceGroups/', parameters('targetResourceGroup') , '/providers/Microsoft.Logic/workflows/', variables('logicAppName') ,'/triggers/', variables('logicAppTriggerName'))]"
},
"resources": [
{
"apiVersion": "2019-01-01-preview",
"name": "[parameters('ascWorkflowName')]",
"type": "Microsoft.Security/automations",
"location": "westeurope",
"tags": {},
"properties": {
"description": "Workflow to push security center recommendations to our logicApp that routes it to serviceNow",
"isEnabled": true,
"scopes": [
{
"description": "[concat('scope for current subscriptionId:', subscription().subscriptionId)]",
"scopePath": "[concat('/subscriptions/',subscription().subscriptionId)]"
}
],
"sources": [
{
"eventSource": "Assessments",
"ruleSets": [
{
"rules": [
{
"propertyJPath": "type",
"propertyType": "String",
"expectedValue": "Microsoft.Security/assessments",
"operator": "Contains"
}
]
}
]
}
],
"actions": [
{
"logicAppResourceId": "[variables('logicAppResourceId')]",
"actionType": "LogicApp",
"uri": "[listCallbackUrl(variables('ascWorkflowTriggerId'), '2016-06-01').value]"
}
]
}
}
]
}
}
}
}
}
}
With this setup I would expect that the resourceGroup parameter reference links to the parameter of the parent policy-set /initiative.
But what I get is an error using the azure-cli in powershell. Why do I get the error?
function ConvertTo-PolicyJson {
param (
[PSCustomObject] $inputObject
)
# See this issue with convertto-json array serialization problem -
# https://stackoverflow.com/questions/20848507/why-does-powershell-give-different-result-in-one-liner-than-two-liner-when-conve/38212718#38212718
# Remove the redundant ETS-supplied .Count property
$removed = Remove-TypeData System.Array -erroraction 'silentlycontinue'
$json = ConvertTo-Json $inputObject -Depth 10
return $json.replace('"', '\"').replace("`n","").replace("`r","" )
}
...
$policyRuleParametersJson = ConvertTo-PolicyJson #{
"resourceGroup" = #{
"type" = "String"
"defaultValue" = "$ResourceGroup"
"metadata" = #{
"description" = "The resource group where the resources are located in"
"displayName" = "Resource group"
"strongType" = "existingResourceGroups"
}
}}
...
$policySetJson = ConvertTo-PolicyJson #(
#{
"policyDefinitionId" = "/subscriptions/$Subscription/providers/Microsoft.Authorization/policyDefinitions/$ResourceGroupExistsPolicyName"
"parameters" = #{
"resourceGroup" = #{
"value" = "my_resource_group"
} }
...
$policyDefinitionSetCreateResult = az policy set-definition create `
--subscription $Subscription `
--definitions $policySetJson `
--params $policyRuleParametersJson `
--name $PolicySetName `
--description $PolicySetDescription `
--display-name $PolicySetDisplayName
The error I am getting is:
The policy set 'my-policy-set' has defined parameters 'resourceGroup' which are not used in referenced policy definitions. Please either remove these parameters from the definition or ensure that they are used.
But I have used the resourceGroup parameter as far as I know.
By the way I condensed the error example to one parameter (resourceGroup). Finally I would like to use more parameters.
Can somebody help please?
Trying to give an answer on my own question.
I guess you need to use uppercase initiative parameter names that do not collide with child parameter names. For instance use RESOURCEGROUP_1 or RESOURCEGROUP if your policy parameter has been named resourceGroup. There seems to be an implicit naming convention for those parameter names that has not been documented properly.

Configure SSL on Azure Web App using ARM. The parameter {0} has an invalid value. ExtendedCode 51008,

I am trying to configure SSL and custom domain name using this ARM Template.
Full error message:
New-AzureRmResourceGroupDeployment : 4:03:36 AM - Resource Microsoft.Web/certificates '<certificateName>' failed with message '{
"Code": "BadRequest",
"Message": "The parameter httpResponseMessage has an invalid value.",
"Target": null,
"Details": [
{
"Message": "The parameter httpResponseMessage has an invalid value."
},
{
"Code": "BadRequest"
},
{
"ErrorEntity": {
"ExtendedCode": "51008",
"MessageTemplate": "The parameter {0} has an invalid value.",
"Parameters": [
"httpResponseMessage"
],
"Code": "BadRequest",
"Message": "The parameter httpResponseMessage has an invalid value."
}
}
],
"Innererror": null
}'
The error message hints to Microsoft.Web/certificates in the ARM template
{
"type":"Microsoft.Web/certificates",
"name":"[parameters('certificateName')]",
"apiVersion":"2016-03-01",
"location":"[parameters('existingAppLocation')]",
"properties":{
"keyVaultId":"[parameters('existingKeyVaultId')]",
"keyVaultSecretName":"[parameters('existingKeyVaultSecretName')]",
"serverFarmId":"[parameters('existingServerFarmId')]"
}
},
The values of those parameters are:
certificateName: 16charstring
existingKeyVaultId: /subscriptions/<subscriptionid>/resourceGroups/<ressourcegroupname>/providers/Microsoft.KeyVault/vaults/<VaultName>
existingKeyVaultSecretName: https://<VaultName>.vault.azure.net:443/secrets/<certificateName>/12345678901234567890
existingServerFarmId: /subscriptions/<subscriptionid>/resourceGroups/<ressourcegroupname>/providers/Microsoft.Web/serverFarms/<AppServicePlanName>
I am using the Invoke-AddCertToKeyVault cmdlet found in RPHelper library to add the certicate to the vault
Write-Host "Reading pfx file from $ExistingPfxFilePath"
$cert = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 $ExistingPfxFilePath, $Password
$bytes = [System.IO.File]::ReadAllBytes($ExistingPfxFilePath)
$base64 = [System.Convert]::ToBase64String($bytes)
$jsonBlob = #{
data = $base64
dataType = 'pfx'
password = $Password
} | ConvertTo-Json
$contentbytes = [System.Text.Encoding]::UTF8.GetBytes($jsonBlob)
$content = [System.Convert]::ToBase64String($contentbytes)
$secretValue = ConvertTo-SecureString -String $content -AsPlainText -Force
Write-Host "Writing secret to $CertificateName in vault $VaultName. Secret value " $secretValue
$secret = Set-AzureKeyVaultSecret -VaultName $VaultName -Name $CertificateName -SecretValue $secretValue
$output = #{};
$output.SourceVault = $resourceId;
$output.CertificateURL = $secret.Id;
$output.CertificateThumbprint = $cert.Thumbprint;
Can you tell me what is wrong?
According to your description, I guess there are something wrong with your template certificate parameters.
Since the link you have posted couldn't be accessed. I write a test arm template and it works well.
I suggest you could follow below template to create the web app.
Notice:
I used powershell to enable the 'Microsoft.Web' Resource Provider directly access the azure key Vault.
Login-AzureRmAccount
Set-AzureRmContext -SubscriptionId AZURE_SUBSCRIPTION_ID
Set-AzureRmKeyVaultAccessPolicy -VaultName KEY_VAULT_NAME -ServicePrincipalName abfa0a7c-a6b6-4736-8310-5855508787cd -PermissionsToSecrets get
The result:
Then you could use below powershell command to insert the certificate to the KeyVault.
$pfxFilePath = "PFX_CERTIFICATE_FILE_PATH" # Change this path
$pwd = "PFX_CERTIFICATE_PASSWORD" # Change this password
$flag = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable
$collection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
$collection.Import($pfxFilePath, $pwd, $flag)
$pkcs12ContentType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12
$clearBytes = $collection.Export($pkcs12ContentType)
$fileContentEncoded = [System.Convert]::ToBase64String($clearBytes)
$secret = ConvertTo-SecureString -String $fileContentEncoded -AsPlainText –Force
$secretContentType = 'application/x-pkcs12'
Set-AzureKeyVaultSecret -VaultName KEY_VAULT_NAME -Name KEY_VAULT_SECRET_NAME -SecretValue $Secret -ContentType $secretContentType # Change Key Vault name and Secret name
After this operation, you could just use the KeyVaultSecretName to directly access the KeyVault to get the value.
The total template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"webAppName": {
"type": "string",
"metadata": {
"description": "The name of the web app that you wish to create."
}
},
"customHostname": {
"type": "string",
"metadata": {
"description": "The custom hostname that you wish to add."
}
},
"existingKeyVaultId": {
"type": "string",
"metadata": {
"description": "Existing Key Vault resource Id with an access policy to allow Microsoft.Web RP to read Key Vault secrets (Checkout README.md for more information)"
}
},
"existingKeyVaultSecretName": {
"type": "string",
"metadata": {
"description": "Key Vault Secret that contains a PFX certificate"
}
}
},
"variables": {
"appServicePlanName": "[concat(parameters('webAppName'),'-asp-', uniquestring(resourceGroup().id))]",
"certificateName": "[concat(parameters('webAppName'),'-cert-', uniquestring(resourceGroup().id))]"
},
"resources": [
{
"apiVersion": "2016-03-01",
"name": "[variables('appServicePlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"properties": {
"name": "[variables('appServicePlanName')]"
},
"sku": {
"name": "P1",
"tier": "Premium",
"size": "1",
"family": "P",
"capacity": "1"
}
},
{
"apiVersion": "2016-03-01",
"name": "[parameters('webAppName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"properties": {
"name": "[parameters('webAppName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverFarms',variables('appServicePlanName'))]"
},
"dependsOn": [
"[concat('Microsoft.Web/serverFarms/',variables('appServicePlanName'))]"
]
},
{
"type": "Microsoft.Web/certificates",
"name": "[variables('certificateName')]",
"apiVersion": "2016-03-01",
"location": "[resourceGroup().location]",
"properties": {
"keyVaultId": "[parameters('existingKeyVaultId')]",
"keyVaultSecretName": "[parameters('existingKeyVaultSecretName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverFarms',variables('appServicePlanName'))]"
},
"dependsOn": [
"[concat('Microsoft.Web/sites/',parameters('webAppName'))]"
]
},
{
"type": "Microsoft.Web/sites/hostnameBindings",
"name": "[concat(parameters('webAppName'), '/', parameters('customHostname'))]",
"apiVersion": "2016-03-01",
"location": "[resourceGroup().location]",
"properties": {
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', variables('certificateName'))).Thumbprint]"
},
"dependsOn": [
"[concat('Microsoft.Web/certificates/',variables('certificateName'))]"
]
}
]
}
The WebSite.parameters:
{
"$schema": "https://schema.management.azure.com/schemas/2015-08-01/deploymentParameters.json",
"contentVersion": "1.0.0.0",
"parameters": {
"webAppName": {
"value": "yourwebappname"
},
"customHostname": {
"value": "yourcustomdomianname"
},
"existingKeyVaultId": {
"value": "/subscriptions/subscriptionsID/resourceGroups/resourceGroupsName/providers/Microsoft.KeyVault/vaults/vaultsName"
},
"existingKeyVaultSecretName": {
"value": "The key vaults SecretName"
}
}
}
Result:

Import a database using AzureRM Powershell

There is a document named Import a BACPAC file to create a new Azure SQL database using PowerShell that covers how to import a bacpac file into SQL server under ASM.
Is there a way to import a bacpac file into an Azure SQL Server using Azure Resource Management cmdlets.
Following on from #juvchan answer I have been trying to get the following to work.
$update = #{
"operationMode" = "Import"
"storageKey"= "Key"
"storageKeyType" = "Primary"
"administratorLogin"= "adminlogin"
"administratorLoginPassword"= "adminpassword"
"storageUri"= "https://example.blob.core.windows.net/sql/exampleIOSQL-2016-1-23-12-26.bacpac"
}
New-AzureRmResource -ResourceGroupName "resourcegroupname" `
-ResourceType "Microsoft.Sql/servers" `
-Name "sqldbsvr" `
-PropertyObject $update `
-ApiVersion 2015-08-01 `
-Force -Location "westeurope"
Unfortunately I can't get anything but this very helpful error message -
New-AzureRmResource : {"code":"","message":"An error occurred while processing this request.","target":null,"details":[],"innererror":[]}
At current time, the latest Microsoft Azure PowerShell - January 2016 (version 1.1)'s Azure RM module does not have any cmdlets which
can support Azure SQL database import like the Azure Service Management's cmdlet i.e. Start-AzureSqlDatabaseImport
However, there is a workaround which can achieve this in the Azure Resource Manager (ARM) context.
The workaround is to do a Azure Resource Group template deployment with a user-defined ARM template which include the database import resource type.
The proposed workaround consist of a sample PowerShell script, a sample ARM template json and a sample ARM Template parameters json as shown below:
The PowerShell sample code is as below:
Login-AzureRmAccount
$tenantId = "your_tenant_id"
$subscriptionId = "your_subscription_id"
$rgName = "your_rg_name"
$location = "your_location"
$templateFile = "YourARMTemplate.json"
$templateParamFile = "YourARMTemplate.Parameters.json"
Set-AzureRmContext -SubscriptionId $subscriptionId -TenantId $tenantId
New-AzureRmResourceGroup -Location $location -Name $rgName -Force
$deployment = New-AzureRmResourceGroupDeployment -ResourceGroupName $rgName -TemplateFile $templateFile -TemplateParameterFile $templateParamFile -Mode Incremental -Force
The sample ARM template json for Azure SQL database import is as below:
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"variables": {
"dbApiVersion": "2014-04-01-preview",
"resourceGroupLocation": "[resourceGroup().location]",
"dbServerNameTidy": "[toLower(trim(parameters('dbServerName')))]",
"masterDbNameTidy": "[toLower(trim(parameters('masterDbName')))]"
},
"parameters": {
"dbServerName": {
"type": "string"
},
"dbLogin": {
"type": "string"
},
"dbPassword": {
"type": "string"
},
"dbServerVersion": {
"type": "string",
"defaultValue": "12.0"
},
"dbCollation": {
"type": "string",
"defaultValue": "SQL_Latin1_General_CP1_CI_AS"
},
"dbEdition": {
"type": "string",
"defaultValue": "Standard"
},
"dbMaxSize": {
"type": "string",
"defaultValue": "10737418240"
},
"dbServiceObjectiveLevel": {
"type": "string",
"defaultValue": "455330E1-00CD-488B-B5FA-177C226F28B7"
},
"bacpacStorageKey": {
"type": "string"
},
"masterDbName": {
"type": "string"
},
"masterBacpacUrl": {
"type": "string"
},
},
"resources": [
{
"type": "Microsoft.Sql/servers",
"apiVersion": "[variables('dbApiVersion')]",
"properties": {
"administratorLogin": "[parameters('dbLogin')]",
"administratorLoginPassword": "[parameters('dbPassword')]",
"version": "[parameters('dbServerVersion')]"
},
"name": "[variables('dbServerNameTidy')]",
"location": "[variables('resourceGroupLocation')]",
"resources": [
{
"type": "firewallrules",
"apiVersion": "[variables('dbApiVersion')]",
"properties": {
"endIpAddress": "0.0.0.0",
"startIpAddress": "0.0.0.0"
},
"name": "AllowAllWindowsAzureIps",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('dbServerNameTidy'))]"
]
},
{
"type": "databases",
"apiVersion": "[variables('dbApiVersion')]",
"properties": {
"edition": "[parameters('dbEdition')]",
"collation": "[parameters('dbCollation')]",
"maxSizeBytes": "[parameters('dbMaxSize')]",
"requestedServiceObjectiveId": "[parameters('dbServiceObjectiveLevel')]"
},
"name": "[variables('webDbNameTidy')]",
"location": "[variables('resourceGroupLocation')]",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('dbServerNameTidy'))]"
],
"resources": [
{
"type": "extensions",
"apiVersion": "[variables('dbApiVersion')]",
"properties": {
"operationMode": "Import",
"storageKey": "[parameters('bacpacStorageKey')]",
"storageKeyType": "Primary",
"administratorLogin": "[parameters('dbLogin')]",
"administratorLoginPassword": "[parameters('dbPassword')]",
"storageUri": "[parameters('masterBacpacUrl')]"
},
"name": "Import",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers/databases', variables('dbServerNameTidy'), variables('masterDbNameTidy'))]"
]
}
]
}
]
}
]
}
The sample ARM template parameters json for Azure SQL database import is as below:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"dbServerName": {
"value": "<your DB Server Name>"
},
"dbLogin": {
"value": "<Your DB server login name>"
},
"dbPassword": {
"value": "<Your DB server login password>"
},
"bacpacStorageKey": {
"value": "<Your Azure Storage Account Primary key which stores the bacpac blob>"
},
"masterDbName": {
"value": "<your Azure Sql Db name>"
},
"masterBacpacUrl": {
"value": "<Your Azure storage account Bacpac blob file full Url>"
}
}
}
Hope this helps and clarifies.

Resources