In part 1 of this article I have gone through creating Azure Applications Gateways (AGW) using Powershell which is a powerful way of deploying resources on Azure, using recursive functions and methods you could build a complex solution in few lines. Unlike Powershell, JSON is a static solution. It gives you a way of creating a baseline deployment, I still haven’t found a way of controlling sub-resources (which isn’t governed by the JSON Copy() function).
In this article I will go over the same deployment using a JSON template, this only serve as a reference point for your deployment. I will use the same Azure AGW configuration I used in part 1 to keep both deployments consistent.
When building Azure AGW, same prerequisites apply to the JSON method. There is a subtle difference which I will go over when I get to that point.
So this time I have prepared a Visio diagram to simplify our deployment and understand what we are trying to achieve – see below.
The diagram shows a Web server hosting four websites running a combination of HTTP HTTPS with authentication (using Basic authentication) or Anonymous access.
The only thing different in comparison to previous Powershell deployment is the steps we take to process SSL certificates and passing them over to the JSON template.
$pfxCert = Get-Content "pathToYourSSL.pfx" -Encoding Byte $pfxCert = [System.Convert]::ToBase64String($pfxCert)
{ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "applicationGatewayName": { "type": "string", "minLength": 1 }, "location": { "type": "string", "minLength": 1, "metadata": { "description": "Region" } }, "virtualNetworkName": { "type": "string", "minLength": 1, "metadata": { "description": "Application Gateway vnet name" } }, "virtualNetworkResourceGroup": { "type": "string", "minLength": 1, "metadata": { "description": "Application Gateway vnet resource group name" } }, "subnetName": { "type": "string", "minLength": 1, "metadata": { "description": "Application Gateway subnet name" } }, "publicIPAddressName": { "type": "string", "minLength": 1, "metadata": { "description": "PIP resource name" } }, "certData": { "type": "string", "minLength": 1, "metadata": { "description": "SSL data in binary Base64 format" } }, "certPassword": { "type": "securestring", "minLength": 1, "metadata": { "description": "SSL certificate password" } }, "authCert": { "type": "string", "minLength": 1, "metadata": { "description": "Public authentication certificate - in binary Base64 format" } }, "Authhostname": { "type": "string", "minLength": 1, "metadata": { "description": "Backend hostheader/Host SNI names used as part of your HTTPS request" } }, "Noauthhostname": { "type": "string", "minLength": 1, "metadata": { "description": "Backend hostheader/Host SNI names used as part of your HTTPS request" } }, "backendIPAddress": { "type": "string", "minLength": 1, "metadata": { "description": "Backend web server IP address" } } }, "variables": { "applicationGatewayID": "[resourceId('Microsoft.Network/applicationGateways',parameters('applicationGatewayName'))]", "capacity": 2, "skuName": "Standard_Medium", "subnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName'))]", "vnetID": "[resourceId(parameters('virtualNetworkResourceGroup'),'Microsoft.Network/virtualNetworks',parameters('virtualNetworkName'))]" }, "resources": [ { "apiVersion": "2015-06-15", "type": "Microsoft.Network/publicIPAddresses", "name": "[parameters('publicIPAddressName')]", "location": "[parameters('location')]", "properties": { "publicIPAllocationMethod": "Dynamic" }, "tags": { "displayName": "[parameters('publicIPAddressName')]" } }, { "apiVersion": "2016-06-01", "name": "[parameters('applicationGatewayName')]", "type": "Microsoft.Network/applicationGateways", "location": "[resourceGroup().location]", "dependsOn": [ "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]" ], "properties": { "sku": { "name": "[variables('skuName')]", "tier": "Standard", "capacity": "[variables('capacity')]" }, "sslCertificates": [ { "name": "appGatewaySslCert", "properties": { "data": "[parameters('certData')]", "password": "[parameters('certPassword')]", "publicCertData": "[parameters('authCert')]" } } ], "authenticationCertificates": [ { "name": "PublicCert", "properties": { "data": "[parameters('authCert')]" } } ], "gatewayIPConfigurations": [ { "name": "appGatewayIpConfig", "properties": { "subnet": { "id": "[variables('subnetRef')]" } } } ], "frontendIPConfigurations": [ { "name": "appGatewayFrontendIP", "properties": { "PublicIPAddress": { "id": "[resourceId('Microsoft.Network/publicIPAddresses',parameters('publicIPAddressName'))]" } } } ], "frontendPorts": [ { "name": "appGWFEHttps", "properties": { "Port": 443 } }, { "name": "appGWFEHttp", "properties": { "Port": 80 } } ], "probes": [ { "name": "CustomProbe", "properties": { "protocol": "Https", "host": "auth.simplesite.com:443", "path": "/noauth/iisstart.htm", "interval": 5, "timeout": 120, "unhealthyThreshold": 2 } } ], "backendAddressPools": [ { "name": "appGatewayBackendPool", "properties": { "BackendAddresses": [ { "IpAddress": "[parameters('backendIPAddress')]" } ] } } ], "backendHttpSettingsCollection": [ { "name": "appGWBEHttpSettings", "properties": { "Port": 80, "Protocol": "Http", "CookieBasedAffinity": "Disabled" } }, { "name": "appGWBEHttpsSettings", "properties": { "Port": 443, "Protocol": "Https", "CookieBasedAffinity": "Disabled", "probe": { "id": "[concat(variables('applicationGatewayID'), '/probes/CustomProbe')]" }, "authenticationCertificates": [ { "id": "[concat(variables('applicationGatewayID'), '/authenticationCertificates/PublicCert')]" } ] } } ], "httpListeners": [ { "name": "appGatewayHttpListener", "properties": { "FrontendIPConfiguration": { "Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]" }, "FrontendPort": { "Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGWFEHttp')]" }, "Protocol": "Http", "SslCertificate": null } }, { "name": "appGatewayHttpsListenerNoAuth", "properties": { "FrontendIPConfiguration": { "Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]" }, "FrontendPort": { "Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGWFEHttps')]" }, "Protocol": "Https", "hostName": "[parameters('Noauthhostname')]", "SslCertificate": { "Id": "[concat(variables('applicationGatewayID'), '/sslCertificates/appGatewaySslCert')]" }, "RequireServerNameIndication": "true" } }, { "name": "appGatewayHttpsListenerAuth", "properties": { "FrontendIPConfiguration": { "Id": "[concat(variables('applicationGatewayID'), '/frontendIPConfigurations/appGatewayFrontendIP')]" }, "FrontendPort": { "Id": "[concat(variables('applicationGatewayID'), '/frontendPorts/appGWFEHttps')]" }, "Protocol": "Https", "hostName": "[parameters('Authhostname')]", "SslCertificate": { "Id": "[concat(variables('applicationGatewayID'), '/sslCertificates/appGatewaySslCert')]" }, "RequireServerNameIndication": "true" } } ], "requestRoutingRules": [ { "Name": "HTTPrule", "properties": { "RuleType": "Basic", "httpListener": { "id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpListener')]" }, "backendAddressPool": { "id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool')]" }, "backendHttpSettings": { "id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGWBEHttpSettings')]" } } }, { "Name": "HTTPSruleNoAuth", "properties": { "RuleType": "Basic", "httpListener": { "id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpsListenerNoAuth')]" }, "backendAddressPool": { "id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool')]" }, "backendHttpSettings": { "id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGWBEHttpsSettings')]" } } }, { "Name": "HTTPSruleAuth", "properties": { "RuleType": "Basic", "httpListener": { "id": "[concat(variables('applicationGatewayID'), '/httpListeners/appGatewayHttpsListenerAuth')]" }, "backendAddressPool": { "id": "[concat(variables('applicationGatewayID'), '/backendAddressPools/appGatewayBackendPool')]" }, "backendHttpSettings": { "id": "[concat(variables('applicationGatewayID'), '/backendHttpSettingsCollection/appGWBEHttpsSettings')]" } } } ] } } ] }
#1 by KK on 3 April, 2017 - 3:57 pm
Hi Sam,
Need one help, I want to provide the PFX file using above template, if I convert the PFX to base64 then how i can proivde it in parameter. Actually i have not tested yet , but the confusion is, 1)after converting the pfx to base 64 it will be text file or just a string?
2. If it is string then i can easy pass it thorugh VSTS pipeline or parameter file, but if it is text file then how i can pass it to template.
Could you please guide me on that?
Thanks,
KK
#2 by Sam on 4 April, 2017 - 8:18 am
Hi KK
Thank you for your message.
The JSON script expects a .pfx file and not a string, You should add this file to your VS project before you start referencing it in your code.
I don’t use VSTS for deployment but I used GitHub in the past and by ensuring the file lives within your project boundaries then you could always reference it from that same location, or if you are making it available using some other methods then you could always specify the URI to that file.
let me know how you get on and if you need any further assistance with this.
Regards
Sam
#3 by Ajit on 11 December, 2017 - 11:56 pm
Hi Sam,
I am working on somewhat same config deployment.The problem that i am facing is the password for the pfx file after conversion to base64 does not work out. it errors out with below message:
Error: Code=ApplicationGatewaySslCertificateInvalidData; Message=Data or Password is invalid.
any pointers would be helpful.
Regards,
AG.
#4 by Sam on 12 December, 2017 - 4:04 pm
Hi AG
How are you exporting your pfx file? Are you using openssl or doing it via MMC?
Thanks
Sam