Connect Azure Web App to vNet using ARM template - azure

I am trying to script the setup of a bunch of resource in Azure, as part of this I need a web app to be able to communicate with a service running on a VM via the vNet.
I have created a template that seems to do everything it should to create the connection but for some reason the connection is not made. Looking in the portal shows that the site is connected to the vNet and that the certificates are in sync, but the point-to-site configuration on the vNet gateway shows no active connections.
However if I disconnect the web app from the vNet and then use the setup button in the Azure portal to reconnect to the same vNet everything works perfectly.
There must be something I'm missing in my template, but having spent the last few hours looking I cannot work out what
Here is my ARM template
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
},
"resources": [
{
"type": "Microsoft.Network/networkSecurityGroups",
"name": "[variables('nsgName')]",
"apiVersion": "2016-03-30",
"location": "[parameters('location')]",
"properties": {
"securityRules": []
},
"resources": [ ],
"dependsOn": [ ]
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"name": "[variables('infrastructureNsgName')]",
"apiVersion": "2016-03-30",
"location": "[parameters('location')]",
"properties": {
"securityRules": []
},
"resources": [ ],
"dependsOn": [ ]
},
{
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('vnetName')]",
"apiVersion": "2016-03-30",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.1.0.0/16"
]
},
"subnets": [
{
"name": "default",
"properties": {
"addressPrefix": "10.1.0.0/17",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName'))]"
}
}
},
{
"name": "infrastructure",
"properties": {
"addressPrefix": "10.1.254.0/24",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('infrastructureNsgName'))]"
}
}
},
{
"name": "GatewaySubnet",
"properties": {
"addressPrefix": "10.1.128.0/24"
}
}
]
},
"resources": [ ],
"dependsOn": [
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('infrastructureNsgName'))]"
]
},
{
"type": "Microsoft.Web/sites",
"kind": "api",
"name": "[variables('gatewaySiteName')]",
"apiVersion": "2015-08-01",
"location": "[parameters('location')]",
"properties": {
"name": "[variables('gatewaySiteName')]",
"hostNames": [
"[concat(variables('gatewaySiteName'),'.azurewebsites.net')]"
],
"enabledHostNames": [
"[concat(variables('gatewaySiteName'),'.azurewebsites.net')]",
"[concat(variables('gatewaySiteName'),'.scm.azurewebsites.net')]"
],
"hostNameSslStates": [
{
"name": "[concat(variables('gatewaySiteName'),'.azurewebsites.net')]",
"sslState": 0,
"thumbprint": null,
"ipBasedSslState": 0
},
{
"name": "[concat(variables('gatewaySiteName'),'.scm.azurewebsites.net')]",
"sslState": 0,
"thumbprint": null,
"ipBasedSslState": 0
}
],
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('gatewayServerFarmName'))]"
},
"resources": [],
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('gatewayServerFarmName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('vnetName'))]"
]
},
{
"type": "Microsoft.Web/serverfarms",
"sku": {
"name": "S1",
"tier": "Standard",
"size": "S1",
"family": "S",
"capacity": 1
},
"kind": "",
"name": "[variables('gatewayServerFarmName')]",
"apiVersion": "2015-08-01",
"location": "[parameters('location')]",
"properties": {
"name": "[variables('gatewayServerFarmName')]",
"numberOfWorkers": 1
},
"resources": [ ],
"dependsOn": [ ]
},
{
"name": "[variables('vnetGatewayIpName')]",
"type": "Microsoft.Network/publicIPAddresses",
"location": "[parameters('location')]",
"apiVersion": "2015-06-15",
"properties": {
"publicIPAllocationMethod": "Dynamic"
}
},
{
"name": "[variables('vnetGatewayName')]",
"type": "Microsoft.Network/virtualNetworkGateways",
"location": "[parameters('location')]",
"apiVersion": "2015-06-15",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('vnetGatewayIpName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('vnetName'))]"
],
"properties": {
"ipConfigurations": [
{
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets',variables('vnetName'),'GatewaySubnet')]"
},
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('vnetGatewayIpName'))]"
}
},
"name": "vnetGatewayConfig"
}
],
"gatewayType": "Vpn",
"vpnType": "RouteBased",
"enableBgp": false,
"vpnClientConfiguration": {
"vpnClientAddressPool": {
"addressPrefixes": [
"172.16.201.0/24"
]
},
"vpnClientRootCertificates": [
{
"name": "AppServiceCertificate.cer",
"properties": {
"PublicCertData": "[reference(concat('Microsoft.Web/sites/', variables('gatewaySiteName'), '/virtualNetworkConnections/virtualNetworkConnections')).certBlob]"
}
}
]
}
}
},
{
"name": "[variables('gatewayVnetConnectionName')]",
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"location": "[parameters('location')]",
"apiVersion": "2015-08-01",
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('gatewaySiteName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('vnetName'))]"
],
"properties": {
"vnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
}
}
]
}

I could never make that work just by the ARM template alone.
However if you can spend one more PowerShell command post-creation, it works beautifully:
# Set VNET Integration for Web App
$ResourceGroup = "WeMadeThatInWestEuropeDidntWe"
$WebApp = "LearningMomentsInProduction"
$PropertiesObject = #{
vnetName = "JimAreYouSureThisIsTheStagingVNET";
}
Set-AzureRmResource -PropertyObject $PropertiesObject `
-ResourceGroupName $ResourceGroup `
-ResourceType Microsoft.Web/sites/config `
-ResourceName $WebApp/web `
-ApiVersion 2015-08-01 -Force -Verbose |
Select -expand Properties |
Select VnetName
# Expected output:
#
# VnetName
# --------
# JimAreYouSureThisIsTheStagingVNET
#
# At this point your Web App is hooked up to the VNET
EDIT:
This does not do what i thought it does.
To resync Point-to-site certificates:
$ResourceGroup = "WeMadeThatInWestEuropeDidntWe"
# VNET Name or Gateway name, try with gateway name!
$vnetName = "JimAreYouSureThisIsTheStagingVNET";
$PropertiesObject = #{
resyncRequired = "true"
}
Set-AzureRmResource -PropertyObject $PropertiesObject -ResourceGroupName $ResourceGroup `
-ResourceType Microsoft.Web/sites/virtualNetworkConnections `
-ResourceName $VnetName
-ApiVersion 2015-08-01 `
-Force -Verbose

A good way of finding the correct settings after you have configured it correct in the Azure Portal, is to take a look under the hood. This can be done by taking a look through https://resources.azure.com/ or the Resource Explorer in the Azure portal.
Here you will find the json in the state it is working and compare those to your ARM template. The settings can't be copied 1-on-1, but it comes close. Good luck with finding the difference.

You put it as a nested resource within the site:
(This assumes everything is under the same subscription and resource group, else, you will need to modify the parameters for resourceId())
"properties":[],
"resources": [
{
"name": "[concat(variables('webappSiteName'), '/', variables('webappSiteName'), '-vnetIntegration')]",
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"apiVersion": "2018-02-01",
"properties": {
"vnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks', 'vnetname')]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('webappSiteName'))]"
]
}
]
At minimum, that what's required, however, the Microsoft.Web/sites/virtualNetworkConnections resource must have the client certificate data provided (which must be configured in the P2S connection in your vnet gateway), which is the property certBlob which according to the documentation it is:
A certificate file (.cer) blob containing the public key of the private key used to authenticate a Point-To-Site VPN connection.
Having that subnode worked for me, if you don't specify the certificate information, your web application(s) will show an error saying the certificates are not in sync.
Refer to the virtualNetworkConnections documentation online
Hope this helps.

Related

Why is my ARM-deployment in a Invalidrequestformat

ARM template
Hey guys, we're trying out to implement a few new templates where we deploy a private Endpoint in an existing subnet. We've successfully set the PrivateEndpoint policies property using ARM, however when deploying the private Endpoint resource we run into a problem:
"resources": [
{
"name": "[variables('privateEndpointName')]",
"location": "[resourceGroup().location]",
"type": "Microsoft.Network/privateEndpoints",
"apiVersion": "2019-04-01",
"properties": {
"subnet": {
"id": "[parameters('subnetId')]"
},
"PrivateLinkServiceConnections": [
{
"properties": {
"privateLinkServiceId": "[parameters('privateLinkResource')]",
"groupIds": "[parameters('targetSubResource')]",
"requestMessage": "[parameters('requestMessage')]"
}
}
]
},
"tags": {
}
}
]
The parameters fed to the template are identical to deployment when using the portal and contain full resource URI's. Deploying to another resource, storage account or SQL has the same outcome.
We've verified the variable privateEndpointName using an empty deployment generating just output. So that's not the issue, but we still receive the following error:
Error
New-AzResourceGroupDeployment : 11:56:20 - Resource Microsoft.Network/privateEndpoints 'privateEndpointSubnet-pe-nameofthesqlserver' failed with message '{
"error": {
"code": "InvalidRequestFormat",
"message": "Cannot parse the request.",
"details": []
}
}'
Portal Template
Deployment with this using the portal is successful
"resources": [
{
"location": "[parameters('location')]",
"name": "[parameters('privateEndpointName')]",
"type": "Microsoft.Network/privateEndpoints",
"dependsOn": [
"[parameters('subnetDeploymentName')]"
],
"apiVersion": "2019-04-01",
"properties": {
"subnet": {
"id": "[parameters('subnet')]"
},
"privateLinkServiceConnections": [
{
"name": "[parameters('privateEndpointName')]",
"properties": {
"privateLinkServiceId": "[parameters('privateLinkResource')]",
"groupIds": "[parameters('targetSubResource')]"
}
}
]
},
"tags": {}
},
{
"apiVersion": "2017-05-10",
"name": "[parameters('subnetDeploymentName')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('virtualNetworkResourceGroup')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"name": "tst2testst-weu-vnet-t/subnet-1",
"id": "/subscriptions/removedsubid/resourceGroups/blabla3/providers/Microsoft.Network/virtualNetworks/tst2testst-weu-vnet-t/subnets/subnet-1",
"properties": {
"provisioningState": "Succeeded",
"addressPrefix": "192.168.0.0/24",
"networkSecurityGroup": {
"id": "/subscriptions/removedsubid/resourceGroups/blabla3/providers/Microsoft.Network/networkSecurityGroups/vnet-id-nsg"
},
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Disabled",
"privateLinkServiceNetworkPolicies": "Enabled"
},
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2019-04-01"
}
]
}
}
}
]
Fixed!
privateLinkServiceConnections JSON-object also requires a name, doesn't look required in the Private Endpoint Arm reference. I'll set up a GitHub issue for it.

Azure tagging a resource group using an ARM template

How can I tag an Azure resource group using an ARM template and use Azure DevOps task Azure Deployment: Create Or Update Resource Group. I have error No HTTP resource was found that matches the request URI
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"customTags": {
"type": "object",
"defaultValue": {
"Environment Name": "TRdev",
"Environment Type":"Dev",
"Product family":"RT"
}
},
"rgName": {
"type": "string",
"defaultValue": "dev-rg",
"metadata": {
"description": "Name of the resourceGroup to create"
}
},
"serverfarms_environment_sp_sku": {
"defaultValue": "B1",
"allowedValues": [ "B1", "S1", "P1V2", "P2V2", "P3V2"],
"type": "String"
},
"serverfarms_environment_sp_name": {
"defaultValue": "dev-sp",
"type": "String"
},
"sites_environment_api_name": {
"defaultValue": "dev-api",
"type": "String"
},
"sites_environment_ui_name": {
"defaultValue": "dev-ui",
"type": "String"
}
},
"variables": { },
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2018-05-01",
"location": "West US",
"name": "[parameters('rgName')]",
"tags": "[parameters('customTags')]",
"properties": {}
},
{
"apiVersion": "2019-08-01",
"name": "[parameters('rgName')]",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('rgName')]",
"tags": "[parameters('customTags')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"parameters": {},
"resources": [
{
"apiVersion": "2018-02-01",
"type": "Microsoft.Web/serverfarms",
"kind": "app",
"name": "[parameters('serverfarms_environment_sp_name')]",
"location": "[resourceGroup().location]",
"tags": "[parameters('customTags')]",
"properties": {},
"dependsOn": [],
"sku": {
"name": "[parameters('serverfarms_environment_sp_sku')]"
}
},
{
"apiVersion": "2018-11-01",
"type": "Microsoft.Web/sites",
"kind": "app",
"name": "[parameters('sites_environment_api_name')]",
"location": "[resourceGroup().location]",
"tags": "[parameters('customTags')]",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
]
},
{
"apiVersion": "2018-11-01",
"type": "Microsoft.Web/sites",
"kind": "app",
"name": "[parameters('sites_environment_ui_name')]",
"location": "[resourceGroup().location]",
"tags": "[parameters('customTags')]",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
]
}
]
},
"parameters": {}
}
}
],
"outputs": {
"sites_environment_api_name": {
"type": "string",
"value": "[parameters('sites_environment_api_name')]"
},
"sites_environment_ui_name": {
"type": "string",
"value": "[parameters('sites_environment_ui_name')]"
}
}
}
Error
error No HTTP resource was found that matches the request URI 'https://management.azure.com/subscriptions/subscriptionsID/resourcegroups/resourcegroupsNeme/providers/Microsoft.Resources/resourceGroups/resourcegroupsNeme?api-version=2018-05-01'.
Thanks.
{
"type": "Microsoft.Resources/tags",
"name": "default",
"apiVersion": "2021-04-01",
"properties": {
"tags": "[variables('resourceTags')]"
}
}
You can apply tags to the target resource group by using the "tags" resource. In this case I've used a variable to store my default tags, but you could explicitly define them as well.
Checkout the following for more details:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json#apply-tags-to-resource-groups-or-subscriptions
What you're trying to achieve is referred to as a subscription level deployment. If you are trying to deploy this through a template via the portal I'm afraid you're out of luck. The deployment UI insists you specify a resource group to deploy in to which invalidates the API path routing when making the call to create your resource group.
To overcome this you'll need to use PowerShell or the Azure CLI.
Another issue with subscription level deployments is that you can't use the resourceGroup() function, so your template will need be adjusted.
Your template should be:
{
"$schema":"https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion":"1.0.0.0",
"parameters":{
"customTags":{
"type":"object",
"defaultValue":{
"Environment Name":"TRdev",
"Environment Type":"Dev",
"Product family":"RT"
}
},
"rgName":{
"type":"string",
"defaultValue":"dev-rg",
"metadata":{
"description":"Name of the resourceGroup to create"
}
},
"serverfarms_environment_sp_sku":{
"defaultValue":"B1",
"allowedValues":[
"B1",
"S1",
"P1V2",
"P2V2",
"P3V2"
],
"type":"String"
},
"serverfarms_environment_sp_name":{
"defaultValue":"dev-sp",
"type":"String"
},
"sites_environment_api_name":{
"defaultValue":"dev-api",
"type":"String"
},
"sites_environment_ui_name":{
"defaultValue":"dev-ui",
"type":"String"
}
},
"variables":{
},
"resources":[
{
"type":"Microsoft.Resources/resourceGroups",
"apiVersion":"2018-05-01",
"location":"West US",
"name":"[parameters('rgName')]",
"tags":"[parameters('customTags')]",
"properties":{
}
},
{
"apiVersion":"2019-08-01",
"name":"[parameters('rgName')]",
"type":"Microsoft.Resources/deployments",
"resourceGroup":"[parameters('rgName')]",
"tags":"[parameters('customTags')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('rgName'))]"
],
"properties":{
"mode":"Incremental",
"template":{
"$schema":"https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion":"1.0.0.1",
"resources":[
{
"apiVersion":"2018-02-01",
"type":"Microsoft.Web/serverfarms",
"kind":"app",
"name":"[parameters('serverfarms_environment_sp_name')]",
"location":"[resourceGroup().location]",
"tags":"[parameters('customTags')]",
"properties":{
},
"dependsOn":[
],
"sku":{
"name":"[parameters('serverfarms_environment_sp_sku')]"
}
},
{
"apiVersion":"2018-11-01",
"type":"Microsoft.Web/sites",
"kind":"app",
"name":"[parameters('sites_environment_api_name')]",
"location":"[resourceGroup().location]",
"tags":"[parameters('customTags')]",
"properties":{
"serverFarmId":"[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
},
"dependsOn":[
"[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
]
},
{
"apiVersion":"2018-11-01",
"type":"Microsoft.Web/sites",
"kind":"app",
"name":"[parameters('sites_environment_ui_name')]",
"location":"[resourceGroup().location]",
"tags":"[parameters('customTags')]",
"properties":{
"serverFarmId":"[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
},
"dependsOn":[
"[resourceId('Microsoft.Web/serverfarms', parameters('serverfarms_environment_sp_name'))]"
]
}
]
},
}
}
],
"outputs":{
"sites_environment_api_name":{
"type":"string",
"value":"[parameters('sites_environment_api_name')]"
},
"sites_environment_ui_name":{
"type":"string",
"value":"[parameters('sites_environment_ui_name')]"
}
}
}
You can save this file to your local machine and deploy as follows.
PowerShell:
New-AzDeployment -TemplateFile '<path-to-template>' -Location 'West US'
Azure CLI:
az deployment create --template-file "<path-to-template>" --location "US West"
You will see a warning about upcoming breaking changes. Microsoft are introducing a mandatory parameter ScopeType to the PowerShell, and scope-type to the CLI with four possible values - ResourceGroup, Subscription, ManagementGroup and Tenant. In this instance, you would set ScopeType to Subscription, but you can see where Microsoft are going with the others.
I expect the portal template deployment UI will be updated soon.
Alternatively the ARM template can remain as is and have an additional step after to run just some simple powershell like:
Set-AzResourceGroup -Name "YOURRESOURCEGROUPNAME" -Tag #{Department="IT"}
This would allow you to maintain the current CI/CD structure.
More information on using Powershell to update the Resource Group

ARM Template for to configure App Services with new VNet Integration feature?

I am working on ARM Templates, I have created the template file with two or more azure app services along with app service plan and then configured with VNET Integration of each app service.
This is sample JSON code:
{
"comments": "Web-App-01",
"name": "[variables('app_name_01')]",
"type": "Microsoft.Web/sites",
"location": "[variables('location')]",
"apiVersion": "2016-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('asp_name_01'))]"
],
"tags": {
"displayName": "[variables('app_name_01')]"
},
"properties": {
"name": "[variables('app_name_01')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('asp_name_01'))]",
"siteConfig": {
"alwaysOn": true
}
},
"resources": [
{
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"name": "[concat(variables('app_name_01'), '/', variables('vnet_connection_name'),uniqueString('asdsdaxsdsd'))]",
"apiVersion": "2016-08-01",
"location": "[variables('location')]",
"properties": {
"vnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vm_vnet_name'), variables('web_subnet_name'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('app_name_01'))]",
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vm_vnet_name'), variables('web_subnet_name'))]"
]
}
]
},
{
"comments": "Web-App-02",
"name": "[variables('app_name_02')]",
"type": "Microsoft.Web/sites",
"location": "[variables('location')]",
"apiVersion": "2016-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('asp_name_02'))]"
],
"tags": {
"displayName": "[variables('app_name_02')]"
},
"properties": {
"name": "[variables('app_name_02')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('asp_name_01'))]",
"siteConfig": {
"alwaysOn": true
}
},
"resources": [
{
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"name": "[concat(variables('app_name_02'), '/', variables('vnet_connection_name'),uniqueString('asdsdaxsdsd'))]",
"apiVersion": "2016-08-01",
"location": "[variables('location')]",
"properties": {
"vnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vm_vnet_name'), variables('web_subnet_name'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('app_name_02'))]",
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vm_vnet_name'), variables('web_subnet_name'))]"
]
}
]
}
The above code works fine for few azure app services, but for the rest of the app services I am getting internal server error or Conflict or Bad Request during VNET Integration of Azure App Service.
Note: When I deployed the above the JSON Code, the old VNET
integration is configured instead of New VNET (Preview) feature. So, I need to configure New VNET (Preview) feature for each app service.
So, can anyone suggest me how to resolve the above issue.
I've found a working example for this on an Azure Docs GitHub post:
How do we integrate the new vnet integrartion with ARM templates?
Seems to work a different way with the new VNet integration which uses a Microsoft.Web/sites/config sub-resource named virtualNetwork instead of the Microsoft.Web/sites/virtualNetworkConnections sub-resource
As well as a few requirements that need to be set on the target subnet / vnet (described in the link). The integration piece looks something like this:
{
"apiVersion": "2018-02-01",
"type": "Microsoft.Web/sites",
"name": "[parameters('appName')]",
"location": "[resourceGroup().location]",
...
"resources": [
{
"name": "virtualNetwork",
"type": "config",
"apiVersion": "2018-02-01",
"location": "[resourceGroup().location]",
"properties": {
"subnetResourceid": "[parameters('subnetResourceId')]",
"swiftSupported": true
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('appName'))]"
]
}
]
},
Apart from this I've not found much else documented, except for a reference to it in the azure-rest-api-specs which has the "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{name}/networkConfig/virtualNetwork" endpoint defined:
azure-rest-api-specs / WebApps.json
It also seems (as the spec suggests) replacing "type": "config" with "type": "networkConfig" also works.
I've talked to a Prem Engineer of Microsoft.
The Key is to replace the Automation Template
{
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"apiVersion": "2018-11-01",
"name": "[concat(parameters('sites_FelixOFA_name'), '/xxxxxxx_Functions')]",
"location": "West Europe",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('sites_FelixOFA_name'))]"
],
"properties": {
"vnetResourceId": "[concat(parameters('virtualNetworks_FelixODevPremNet_externalid'), '/subnets/Functions')]",
"isSwift": true
}
}
with
{
"type": "Microsoft.Web/sites/networkConfig",
"name": "[concat(parameters('webAppName'),'/VirtualNetwork')]",
"apiVersion": "2016-08-01",
"properties":
{
"subnetResourceId": "[parameters('subnetResourceId')]"
}
}
Where subnetResourceId is the resource id of their subnet – it should look like /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/virtualNetworks/{vnetName}/subnets/{subnetName}
I tried to reach same functionality by Bicep file, finally using
virtualNetworkSubnetId:
worked for me.
For example:
resource webAppName_resource 'Microsoft.Web/sites#2020-12-01' = {
name: '${webAppName}'
location: location
properties: {
serverFarmId: appServicePlanPortalName.id
virtualNetworkSubnetId: '${vnetDeploy_module.outputs.vnetid}/subnets/${vnetDeploy_module.outputs.subnetname}'
siteConfig: {
linuxFxVersion: linuxFxVersion
minTlsVersion: minTlsVersion
http20Enabled: http20Enabled
}
httpsOnly: httpsOnly
}
}
See templates in https://github.com/Azure/azure-quickstart-templates/tree/master/quickstarts/microsoft.web/app-service-regional-vnet-integration.
Result in ARM is:
{
"type": "Microsoft.Web/sites",
"apiVersion": "2021-01-01",
"name": "[parameters('appName')]",
"location": "[parameters('location')]",
"kind": "app",
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
"virtualNetworkSubnetId": "[reference(resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))).subnets[0].id]",
"httpsOnly": true,
"siteConfig": {
"vnetRouteAllEnabled": true,
"http20Enabled": true
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]"
]
}
To fix this, I simply changed the isSwift or swiftSupported option to false.

How to create a new resource in Azure ARM templates?

Ok, I've done everything what described here with ARM template - https://azure.microsoft.com/en-us/documentation/articles/web-sites-integrate-with-vnet. Except one thing - Enabling VNET Integration with a pre-existing VNET.
Can this be done in ARM templates?
Thanks!
Here is a sample template that might help you. It's modified from this quickstart sample in GitHub
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Name of the hosting plan to use in Azure."
}
},
"webSiteName": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Name of the Azure Web app to create."
}
},
"vnetName": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Name of an existing Azure VNet which has a Gateway Subnet already, and is in the resource group you are going to deploy."
}
},
"skuName": {
"type": "string",
"defaultValue": "S1",
"allowedValues": [
"S1",
"S2",
"S3",
"P1",
"P2",
"P3",
"P4"
],
"metadata": {
"description": "Describes plan's pricing tier and instance size. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
}
},
"skuCapacity": {
"type": "int",
"defaultValue": 1,
"minValue": 1,
"metadata": {
"description": "Describes plan's instance count"
}
}
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "[parameters('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "HostingPlan"
},
"sku": {
"name": "[parameters('skuName')]",
"capacity": "[parameters('skuCapacity')]"
},
"properties": {
"name": "[parameters('hostingPlanName')]"
}
},
{
"apiVersion": "2015-08-01",
"name": "[parameters('webSiteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "Website"
},
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"name": "[parameters('webSiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "web",
"type": "config",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"properties": {
"pythonVersion": "3.4"
}
},
{
"apiVersion": "2015-08-01",
"name": "[parameters('vnetName')]",
"type": "virtualNetworkConnections",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"properties": {
"vnetResourceId": "[concat(resourceGroup().id, '/providers/Microsoft.Network/virtualNetworks/', parameters('vnetName'))]"
}
}
]
}
]
}
Here are 3 things you should be careful with.
The template starts with a python web app template, and adds a "Microsoft.Web/sites/virtualNetworkConnections" resource. So, if you are using other programing language, you can start with some other template.
The pre-existing VNet should be in the same resource group you are deploying. If the VNet you are using are not in the same resource group, you should modify the "vnetResourceId" in the "properties" of the "Microsoft.Web/sites/virtualNetworkConnections".
The VNet you are using should have a Gateway with a Point-to-Site address already. Otherwise, you will not be able integrate you web app to the VNet. For more details, see Configure a Point-to-Site connection to a virtual network using PowerShell
Update: About how I get this info, well, there is not much about this on the net. This template is constructed based on the PowerShell solution and my knowledge about ARM template. The PowerShell solution is available in this article. Another possible way to get an ARM template is to create those resources in one resource group, and export the template of the resource group in the portal. But, for this case, that is not going to work, because resource type "Microsoft.Web/sites/virtualNetworkConnections" is not supported yet. However, you can still get a look at the REST API by the PowerShell Command Get-AzureRmResource with option -debug.
Get-AzureRmResource -ResourceGroupName <resource group> -ResourceType Microsoft.Web/sites/virtualNetworkConnections -Name <web app>/<VNet> -debug -ApiVersion 2015-08-01
You will get the following REST API.
Uri:
https://management.azure.com/subscriptions/<subscription id>/resourceGroups/<resource group>/providers/Microsoft.Web/sites/<web app>/virtualNetworkConnections/<VNet>?api-version=2015-08-01
Body:
{
"id": "/subscriptions/<subscription id>/resourceGroups/<resource group>/providers/Microsoft.Web/sites/<web app>/virtualNetworkConnections/<VNet>",
"name": "<VNet>",
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"location": "<Location>",
"tags": null,
"properties": {
"vnetResourceId": "/subscriptions/<subscription id>/resourceGroups/<resource group>/providers/Microsoft.Network/virtualNetworks/<VNet>"
"certThumbprint": "<Thumbprint>",
"certBlob": "<cert>",
"routes": null,
"resyncRequired": false,
"dnsServers": null
}
}
Skipping some automatically generated values, you will get the template which is quite similar to the one I write:
{
"name": "<VNet>",
"type": "Microsoft.Web/sites/virtualNetworkConnections",
"location": "<Location>",
"properties": {
"vnetResourceId": "/subscriptions/<subscription id>/resourceGroups/<resource group>/providers/Microsoft.Network/virtualNetworks/<VNet>"
}
}

Create VM from existing VHD: Preview portal

Does anyone now how to create a VM from an existing VHD in the new azure portal ?
I can find lots of info on how to do this in manage.windowsazure.com, but nothing on this functionality in portal.azure.com.
It can't be done literally in the portal. You will have to use powershell.
Create a storageaccount. For example in the portal.
Upload the VHD to azure. To do this, run the following line in powershell after logging in with Login-AzureRmAccount (change the parameters between <> and the path to the vhd on your harddisk):
Add-AzurermVhd -Destination "https://<StorageAccountName>.blob.core.windows.net/<containerName>/<vhdname>.vhd" -LocalFilePath "D:\Virtual Machines\myharddisk.vhd" -ResourceGroupName "<ResourceGroupName" -Overwrite
Create an ARM template.
Multiple possiblities what you can do.
For example choose a template from the Azure Quickstart templates, for example 101
What I have done is:
Created a new project in Visual Studio 2015.
Choose the following project: Cloud->Azure Resource Group
Choose the following template: Windows Virtual Machine
Changed some parameters and removed all stuff that is not necessary.
What it does now is: Create a Windows Virtual Machine using the uploaded vhd as harddisk.
It's using a parameters json file now, and also some variables have to be set in the WindowsVirtualMachine.json
This could be refactored ofcourse. but for now it will do what's needed.
For this sample you have to have the following directory structure (just like Visual Studio creates it)
ProjectDirectory/Scripts/Deploy-AzureResourceGroup.ps1
ProjectDirectory/Templates/WindowsVirtualMachine.json
ProjectDirectory/Templates/WindowsVirtualMachine.parameters.json
Deploy-AzureResourceGroup.ps1
#Requires -Version 3.0
#Requires -Module AzureRM.Resources
#Requires -Module Azure.Storage
Param(
[string] [Parameter(Mandatory=$true)] $ResourceGroupLocation,
[string] $ResourceGroupName = 'CreateImage',
[string] $TemplateFile = '..\Templates\WindowsVirtualMachine.json',
[string] $TemplateParametersFile = '..\Templates\WindowsVirtualMachine.parameters.json'
)
Import-Module Azure -ErrorAction SilentlyContinue
try {
[Microsoft.Azure.Common.Authentication.AzureSession]::ClientFactory.AddUserAgent("VSAzureTools-$UI$($host.name)".replace(" ","_"), "2.8")
} catch { }
Set-StrictMode -Version 3
$OptionalParameters = New-Object -TypeName Hashtable
$TemplateFile = [System.IO.Path]::Combine($PSScriptRoot, $TemplateFile)
$TemplateParametersFile = [System.IO.Path]::Combine($PSScriptRoot, $TemplateParametersFile)
# Create or update the resource group using the specified template file and template parameters file
New-AzureRmResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Verbose -Force -ErrorAction Stop
New-AzureRmResourceGroupDeployment -Name ((Get-ChildItem $TemplateFile).BaseName + '-' + ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmm')) `
-ResourceGroupName $ResourceGroupName `
-TemplateFile $TemplateFile `
-TemplateParameterFile $TemplateParametersFile `
#OptionalParameters `
-Force -Verbose
WindowsVirtualMachine.json
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"dnsNameForPublicIP": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Globally unique DNS Name for the Public IP used to access the Virtual Machine."
}
}
},
"variables": {
"OSDiskName": "<vhdNameWithoutExtension>",
"vhdStorageContainerName": "<containerName>",
"storageAccountName": "<StorageAccountName>",
"nicName": "myVMNic",
"addressPrefix": "10.0.0.0/16",
"subnetName": "Subnet",
"subnetPrefix": "10.0.0.0/24",
"vhdStorageType": "Standard_LRS",
"publicIPAddressName": "myPublicIP",
"publicIPAddressType": "Dynamic",
"vhdStorageName": "[concat('vhdstorage', uniqueString(resourceGroup().id))]",
"vmName": "MyWindowsVM",
"vmSize": "Standard_A2",
"virtualNetworkName": "MyVNET",
"vnetId": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]",
"subnetRef": "[concat(variables('vnetId'), '/subnets/', variables('subnetName'))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('vhdStorageName')]",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "StorageAccount"
},
"properties": {
"accountType": "[variables('vhdStorageType')]"
}
},
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "PublicIPAddress"
},
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
}
}
},
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "VirtualNetwork"
},
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetPrefix')]"
}
}
]
}
},
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "NetworkInterface"
},
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}
},
{
"apiVersion": "2015-06-15",
"type": "Microsoft.Compute/virtualMachines",
"name": "[variables('vmName')]",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "VirtualMachine"
},
"dependsOn": [
"[concat('Microsoft.Storage/storageAccounts/', variables('vhdStorageName'))]",
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[variables('vmSize')]"
},
"storageProfile": {
"osDisk": {
"name": "osdisk",
"osType": "Windows",
"vhd": {
"uri": "[concat('http://', variables('storageAccountName'), '.blob.core.windows.net/', variables('vhdStorageContainerName'), '/', variables('OSDiskName'), '.vhd')]"
},
"caching": "ReadWrite",
"createOption": "Attach"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
}
}
}
]
}
WindowsVirtualMachine.parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"dnsNameForPublicIP": {
"value": "<someUniqueNameForYourDnsName>"
}
}
}
Execute the powershell script
Open up a Powershell command and execute the ps1 script. You only have to pass the location where you want the vm to be created like: (you should already be logged in with Login-AzureRmAccount)
Before running change the parameters in both json files!
.\Deploy-AzureResourceGroup.ps1 "West Europe"
The logging should tell you that the VM is created successfully.
Today (Oct 2016) it still can´t be done in the new portal.
But for completeness: You can do it in the old portal (https://manage.windowsazure.com):
Click New - Compute - Virtual Machine - From Gallery.
On the left either select MY IMAGES or MY DISKS and select the VHD you want to use.
Follow the instructions as usual.

Resources