Azure Application Gateway forwarding "/*" to Backend Pool - azure

I have an Azure Application Gateway set up with Path-Based routing to route between two different Backend Pools. I also have Application Insights configured on one of the Pools, which I will come back to in a moment. My path rule is configured like this:
/home/* -> Backend Pool 1
/* -> Backend Pool 2
I have never been able to connect to Backend Pool 1 but, I have been able to successfully connect to Backend Pool 2 at /* and when I was able to do that, going to /home/* would still be sent to Backend Pool 2 which didn't exist there. I tried using the Override Backend Path setting on the HTTP Settings, but then neither route would work and I would receive a 502 error. So naturally, I tried to reverse that setting, but nothing would change.
However, I did notice in the Application Insights for Backend Pool 2 that after removing the Override Backend Path setting, that the server pool was receiving the /* as part of the request and thus, receiving a 400 error because that route doesn't exist and the character is not allowed in the URL (It's worth noting that my web.config file doesn't have request URL character restrictions right now).
I know that this type of routing is possible, given the number of documents from Azure, but I've been dealing with this problem for two weeks and have poured over every scrap of documentation and don't seem to be getting anywhere.
So to clarify, my specific question is:
Given the things I've already tried, am I missing something in my configuration, is something wrong about my configuration?
I'd be more than happy to clarify any points that you feel I've left out.
EDIT: Adding the configuration of the one rule and its path map for context.
[
{
"backendAddressPool": null,
"backendHttpSettings": null,
"etag": "<####>",
"httpListener": {
"id": "<####>",
"resourceGroup": "<####>"
},
"id": "<####>",
"name": "HttpsPaths",
"provisioningState": "Succeeded",
"redirectConfiguration": null,
"resourceGroup": "<####>",
"ruleType": "PathBasedRouting",
"type": null,
"urlPathMap": {
"defaultBackendAddressPool": {
"id": "<####>/backendPool1",
"resourceGroup": "<####>"
},
"defaultBackendHttpSettings": {},
"defaultRedirectConfiguration": null,
"etag": "<####>",
"id": "<####>",
"name": "HttpsPaths",
"pathRules": [
{
"backendAddressPool": {
"id": "<####>/backendPool1"
},
"backendHttpSettings": {
"id": "<####>/OverrideBackendPathSettings (redirects to '/' on the backend)",
"resourceGroup": "<####>"
},
"etag": "<####>",
"id": "<#####>",
"name": "home",
"paths": [
"/home/*"
],
"provisioningState": "Succeeded",
"redirectConfiguration": null,
"resourceGroup": "<####>",
"type": null
},
{
"backendAddressPool": {
"id": "<####>/BackendPool2",
"resourceGroup": "<####>"
},
"backendHttpSettings": {
"id": "<####>/appGatewayBackendHttpSettings (sends request as is)",
"resourceGroup": "<####>"
},
"etag": "<####>",
"id": "<####>/gryphon",
"name": "gryphon",
"paths": [
"/*"
],
"provisioningState": "Succeeded",
"redirectConfiguration": null,
"resourceGroup": "<####>",
"type": null
}
],
"provisioningState": "Succeeded",
"resourceGroup": "<####>",
"type": null
},
"provisioningState": "Succeeded",
"resourceGroup": "<####>",
"type": null
}
]

Rules are evaluated in the order they are specified. It could be that you have a basic rule preceding the path based rule. This would cause the basic rule to intercept all traffic and route to backend pool specified in that rule. If that is not the case, then pasting the rules configuration would probably help.
--
Edit
I looked at your configuration details in our monitoring system. This is because of an incorrect probe configuration. You have /* in probes which is not valid. The probe should point to an existing page which returns a 200 http response code. Also you do not need an path override and can be removed. Once you have probes configured correctly, please ensure that the backend health report is showing all backend servers as healthy. Then your path based rules would work as expected.

Related

ARM Template Issues Create resource with cyclical dependancy (Webapp Custom Domain, Self Managed Cert then Bind Cert)

I have a real chicken and egg situation. Also i am quite new to ARM so maybe missing something glaringly obvious.
Previously we had an arm template that worked using a certificate from a keyvault which was fine.
hostnamebindings resources created the custom domain and binding.
However we want to move to use the explicit self managed certificates in azure for the web service but are hitting some issues at the last hurdle.
The certificate is dependant on the custom domain as without it it fails to deploy but we can not reference the same resource twice in the template without it erroring.
Order must be:
Create custom domain
Create certificate
Bind certificate
ARM template extract below.
{
"condition": "[equals(parameters('UseCustomDomain'),'True')]",
"Comments": "If custom domain is selected then add to the webapplication",
"type": "Microsoft.Web/sites/hostnameBindings",
"apiVersion": "2022-03-01",
"name": "[concat(variables('appName'), '/', variables('DomainName'))]",
"location": "[ResourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('appName'))]"
],
"properties": {
"domainId": null,
"hostNameType": "Verified",
"siteName": "variables('DomainName')"
}
},
{
"type": "Microsoft.Web/certificates",
"apiVersion": "2021-03-01",
"name": "[variables('DomainName')]",
"Comments": "Creating Subdomain Certificate",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('AppName'))]",
"[resourceId('Microsoft.Web/sites/hostnameBindings/',variables('appName'), variables('DomainName'))]"
],
"properties": {
"hostNames": [
"[variables('DomainName')]"
],
"canonicalName": "[variables('DomainName')]",
"serverFarmId": "[variables('ServerFarmID')]"
}
},
What I would like to do is add the following properties after the the certificate is created to the hostnamebindings resource.
"sslState": "[if(variables('enableSSL'), 'SniEnabled', json('null'))]",
"thumbprint": "[if(variables('enableSSL'), reference(resourceId('Microsoft.Web/certificates', variables('DomainName'))).Thumbprint, json('null'))]"
Is there a way to make individual properties dependant on a resource? When i try the below in the hostname bindings properities i get a "Deployment template validation failed: 'Circular dependency detected on resource"
"properties": { "domainId": null, "hostNameType": "Verified", "siteName": "variables('DomainName')", "dependsOn": [ "[resourceId('Microsoft.Web/certificates', variables('DomainName'))]" ], "sslState": "[if(variables('enableSSL'), 'SniEnabled', json('null'))]", "thumbprint": "[if(variables('enableSSL'), reference(resourceId('Microsoft.Web/certificates', variables('DomainName'))).Thumbprint, json('null'))]" }
Any help greatly appreciated.

Add a default DNS domain name to azure Web app

I am creating an Azure Web app with the name "CustomerX-app-001" the default custom domain that Azure creates after the creation of the Azure web app is : "Customerx-app-001.azurewebsites.net".
Inside my arm template I've tried to change this default hostname to "Customerx-app.azurewebsites.net" by doing these 2 solutions:
Adding the hostnamebinding resource inside the resource block of microsoft.web/sites
"resources": [
{
"type": "hostNameBindings",
"apiVersion": "2018-11-01",
"name": "[concat(parameters('CustomHostname'), '.azurewebsites.net')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('siteName'))]"
],
"properties": {
"siteName": "[parameters('siteName')]"
}
},
**Adding the hostnamebinding resource outside as a new resource block **
{
"type": "Microsoft.Web/sites/hostNameBindings",
"apiVersion": "2018-11-01",
"name": "[concat(parameters('siteName'), '/', parameters('CustomHostname'), '.azurewebsites.net')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('siteName'))]"
],
"properties": {
"siteName": "[parameters('siteName')]",
"hostNameType": "Verified"
}
}
With CustomHostname being: "Customerx-app" and sitename being "Customerx-app-001"
Both solutions gave me the same error:
"Code": "BadRequest",
"Message": "Too many (2) hostnames in the default DNS zone. Limit is 1.",
"Target": null,
"Details": [
{
"Message": "Too many (2) hostnames in the default DNS zone. Limit is 1."
},
{
"Code": "BadRequest"
},
{
"ErrorEntity": {
"ExtendedCode": "04017",
"MessageTemplate": "Too many ({0}) hostnames in the default DNS zone. Limit is {1}.",
"Parameters": [
"2",
"1"
],
"Code": "BadRequest",
"Message": "Too many (2) hostnames in the default DNS zone. Limit is 1."
}
}
I am stuck at this for a while and figuring out why the problem occurs.
I think that the azure web app has 1 default DNS name that you can't change and that is always the name of the web app. If another DNS name needs to be added a new DNS record should be made and this record can be added to the web app. But solution 2 does exactly that with the only difference that the DNS name does not exist.
Is there anyone who can help me out here, or guide me in the right direction ?
You can only use a single *.azurewebsites.net dns name and it is being autogenerated. You can only add dns names on a domain you own (and you'd have to validate it first).

Azure SQL failover group, what does the grace period mean?

I am currently reading this: https://learn.microsoft.com/en-us/azure/sql-database/sql-database-auto-failover-group, and I have a hard time understanding the automatic failover policy:
By default, a failover group is configured with an automatic failover
policy. The SQL Database service triggers failover after the failure
is detected and the grace period has expired. The system must verify
that the outage cannot be mitigated by the built-in high availability
infrastructure of the SQL Database service due to the scale of the
impact. If you want to control the failover workflow from the
application, you can turn off automatic failover.
When defining the failover group in an ARM template:
{
"condition": "[equals(parameters('redundancyId'), 'pri')]",
"type": "Microsoft.Sql/servers",
"kind": "v12.0",
"name": "[variables('sqlServerPrimaryName')]",
"apiVersion": "2014-04-01-preview",
"location": "[parameters('location')]",
"properties": {
"administratorLogin": "[parameters('sqlServerPrimaryAdminUsername')]",
"administratorLoginPassword": "[parameters('sqlServerPrimaryAdminPassword')]",
"version": "12.0"
},
"resources": [
{
"condition": "[equals(parameters('redundancyId'), 'pri')]",
"apiVersion": "2015-05-01-preview",
"type": "failoverGroups",
"name": "[variables('sqlFailoverGroupName')]",
"properties": {
"serverName": "[variables('sqlServerPrimaryName')]",
"partnerServers": [
{
"id": "[resourceId('Microsoft.Sql/servers/', variables('sqlServerSecondaryName'))]"
}
],
"readWriteEndpoint": {
"failoverPolicy": "Automatic",
"failoverWithDataLossGracePeriodMinutes": 60
},
"readOnlyEndpoint": {
"failoverPolicy": "Disabled"
},
"databases": [
"[resourceId('Microsoft.Sql/servers/databases', variables('sqlServerPrimaryName'), variables('sqlDatabaseName'))]"
]
},
"dependsOn": [
"[variables('sqlServerPrimaryName')]",
"[resourceId('Microsoft.Sql/servers/databases', variables('sqlServerPrimaryName'), variables('sqlDatabaseName'))]",
"[resourceId('Microsoft.Sql/servers', variables('sqlServerSecondaryName'))]"
]
},
{
"condition": "[equals(parameters('redundancyId'), 'pri')]",
"name": "[variables('sqlDatabaseName')]",
"type": "databases",
"apiVersion": "2014-04-01-preview",
"location": "[parameters('location')]",
"dependsOn": [
"[variables('sqlServerPrimaryName')]"
],
"properties": {
"edition": "[variables('sqlDatabaseEdition')]",
"requestedServiceObjectiveName": "[variables('sqlDatabaseServiceObjective')]"
}
}
]
},
{
"condition": "[equals(parameters('redundancyId'), 'pri')]",
"type": "Microsoft.Sql/servers",
"kind": "v12.0",
"name": "[variables('sqlServerSecondaryName')]",
"apiVersion": "2014-04-01-preview",
"location": "[variables('sqlServerSecondaryRegion')]",
"properties": {
"administratorLogin": "[parameters('sqlServerSecondaryAdminUsername')]",
"administratorLoginPassword": "[parameters('sqlServerSecondaryAdminPassword')]",
"version": "12.0"
}
}
I specify the readWriteEndpoint like this:
"readWriteEndpoint": {
"failoverPolicy": "Automatic",
"failoverWithDataLossGracePeriodMinutes": 60
}
With a failoverWithDataLossGracePeriodMinutes set to 60 minutes.
What does this mean? I cannot find a clear answer anywhere. Does it mean that:
When an outage is happening in my primary region where my primary database resides, the read/write endpoint points to the primary and only after 60 minutes it fails over to my secondary, which becomes the new primary. In the 60 minutes, the only way to read my data is to use the readOnlyEndpoint directly? OR
My read/write endpoint is turned instantly, if they somehow can detect that there was no data to be synced
I think it boils down to: do I have to manually make the failover, if I detect an outage, if I don't care about data loss, but I want to be able to write to my database?
Bonus question: is the reason why the grace period is present because there can be unsynced data on the primary, that will be overwritten, or tossed away, if the secondary becomes the new primary (if i switch manually)?
Sorry, I can't keep it to only one question. I have read a lot and I really need to know this.
What does this mean?
It means that:
"when a outage is happening in my primary region where my primary database resides, the read/write endpoint points to the primary and only after 60 minutes it fails over to my secondary, which becomes the new primary. "
It can't failover automatically even when the data is synced because the high-availability solution in the primary region is trying to do the same thing, and almost all of the time your primary database will come back quickly in the primary region. Performing an automatic cross-region fail-over would interfere with this.
And
"the reason why the grace period is present, is that because the there can be unsynced data on the primary, that will be overwritten, or tossed away, if the secondary becomes the new primary"
And to allow time for the database to failover within the primary region.

CosmosDB Table API - CORS rules are not supported for this API

We have some Azure Table storage tables in our subscription and would like to migrate them to CosmosDB table API due to performance reasons. To do this, I started creating cosmos db account by selecting Table API but my deployment failed with the following error. When i tried with SQL API, it works.
{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. "details":[{"code":"BadRequest","message":"{\r\n \"code\": \"BadRequest\",\r\n \"message\": \"CORS rules are not supported for this API\rMicrosoft.Azure.Documents.Common/2.1.0.0\"\r\n}"}]}
Can someone please let me know what could be the reason for this?
#AngiSen, may be related to a recent (breaking) update of Azure Cosmos DB resource provider (Microsoft.DocumentDb/databaseAccounts) as I just noticed today (28th of Nov 2018) that a previously running deployment (as of 23th of Nov 2018) of Cosmos DB Table API is now failing with this same error:
9:16:23 AM - Resource Microsoft.DocumentDb/databaseAccounts 'xxx-xxx-xxx' failed with message '{
"code": "BadRequest",
"message": "CORS rules are not supported for this API\r\nActivityId: xxx, Microsoft.Azure.Documents.Common/2.1.0.0"
}'
In my case I'm using 2015-04-08 version with Table API but I don't configure explicitly the CORS part and anyway there's no such configuration option in the resource provider.
Digging into the existing Cosmos DB instance with https://resources.azure.com shows there's indeed a CORS member that is part of the definition:
{
"id": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.DocumentDB/databaseAccounts/xxx",
"name": "xxx",
"location": "North Europe",
"type": "Microsoft.DocumentDB/databaseAccounts",
"kind": "GlobalDocumentDB",
"tags": {},
"properties": {
"provisioningState": "Succeeded",
"documentEndpoint": "https://xxx.documents.azure.com:443/",
"tableEndpoint": "https://xxx.table.cosmosdb.azure.com:443/",
"ipRangeFilter": "",
"enableAutomaticFailover": false,
"enableMultipleWriteLocations": false,
"isVirtualNetworkFilterEnabled": false,
"virtualNetworkRules": [],
"EnabledApiTypes": "Table, Sql",
"databaseAccountOfferType": "Standard",
"consistencyPolicy": {
"defaultConsistencyLevel": "BoundedStaleness",
"maxIntervalInSeconds": 86400,
"maxStalenessPrefix": 1000000
},
"configurationOverrides": {},
"writeLocations": [
{
"id": "xxx-northeurope",
"locationName": "North Europe",
"documentEndpoint": "https://xxx-northeurope.documents.azure.com:443/",
"provisioningState": "Succeeded",
"failoverPriority": 0
}
],
"readLocations": [
{
"id": "xxx-northeurope",
"locationName": "North Europe",
"documentEndpoint": "https://xxx-northeurope.documents.azure.com:443/",
"provisioningState": "Succeeded",
"failoverPriority": 0
}
],
"locations": [
{
"id": "xxx-northeurope",
"locationName": "North Europe",
"documentEndpoint": "https://xxx-northeurope.documents.azure.com:443/",
"provisioningState": "Succeeded",
"failoverPriority": 0
}
],
"failoverPolicies": [
{
"id": "xxx-northeurope",
"locationName": "North Europe",
"failoverPriority": 0
}
],
"cors": [],
"capabilities": [
{
"name": "EnableTable"
}
]
}
}
Hope it'll get fixed quickly if it's indeed a breaking change...
Wanted to make an official statement here. I have spoken with the Cosmos DB team and they have a fix ready and it should be deployed tonight. Please let me know if you should have any questions. Thank you for posting the issue.

What are the properties in an ARM template for a Dynamics 365 CRM logic app connector?

Logic app connectors are closed source and the 'Automation Script' option in the Azure portal strips the authentication portions of the properties node from connectors. This is what the portal hands you when you script out the ARM template for a logic app which talks to CRM.
{
"comments": "Generalized from resource: '/subscriptions/<guid>/resourceGroups/<resource group name>/providers/Microsoft.Web/connections/dynamicsCRMconnector'.",
"type": "Microsoft.Web/connections",
"name": "[parameters('connections_dynamicsCRMconnector_name')]",
"apiVersion": "2016-06-01",
"location": "eastus",
"scale": null,
"properties": {
"displayName": "CRMConnection",
"customParameterValues": {},
"api": {
"id": "/subscriptions/<guid>/providers/Microsoft.Web/locations/eastus/managedApis/dynamicscrmonline"
}
},
"dependsOn": []
}
The other connectors (SFTP, storage account, etc.) have the missing elements node documented here and there (nothing official from MS, but blog posts and sample code) but I can't find the information for the Dynamics connectors. As an example of what I would expect to see, here is how SFTP and storage accounts can be pre-configured with authentication values in ARM:
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "[variables('sftp_conn_friendly_name')]",
"location": "[resourceGroup().location]",
"properties": {
"displayName": "SFTP connection",
"parameterValues": {
"hostName": "[variables('sftp_host')]",
"userName": "[variables('sftp_user')]",
"password": "[variables('sftp_pass')]",
"portNumber": "[variables('sftp_port')]",
"giveUpSecurityAndAcceptAnySshHostKey": true,
"disableUploadFilesResumeCapability": false
},
"api": {
"id": "[variables('sftp_conn_managed_id')]"
}
}
},
{
"type": "Microsoft.Web/connections",
"apiVersion": "2016-06-01",
"name": "[variables('storage_conn_friendly_name')]",
"location": "[resourceGroup().location]",
"properties": {
"displayName": "Blob connection",
"parameterValues": {
"accountName": "[variables('storage_account_name')]",
"accessKey": "[listKeys(variables('storage_account_name'),'2015-05-01-preview').key1]"
},
"api": {
"id": "[variables('storage_conn_managed_id')]"
}
}
}
While not a direct answer to your question, but a more general answer giving you idea how to act in such a situation. If its not documented anywhere your only hope is reversing it (and more often than not it works).
First of all, this connecter is a resource in Azure (like the ones you've written). You can use any of the available ways to get the resource properties (https://resource.azure.com, Get-AzureRmResource, REST API, various SDKs) and see what the values are like there.
Another way of going about this - creating this connector using the portal and capturing traffic with fiddler. That way you will see the exact REST call needed to créate such a connector and would be able to replicate it using ARM Template. You might not know that ARM Templates are basically proxies for REST calls. Each resource you are creating is being converted to a REST call and performed against the appropriate resource provider.

Resources