Deploy AMPLS with Terraform
![Deploy AMPLS with Terraform](/content/images/size/w1200/2024/05/launde-morel-4VSsq9ErzOs-unsplash.jpg)
Azure Monitor Private Link Scope (AMPLS) is a very convenient Azure service that amalgamates the various services required for Azure Monitor and makes them available via private endpoints within your virtual network (VNet). This enables completely private interactions with all aspects of the Azure Monitor service, from sending logs to workspaces and storage accounts to running queries. It also allows you to connect on-premises infrastructure to Azure Monitor over ExpressRoute. These features make AMPLS particularly attractive to enterprise and regulated consumers of Azure.
If you try setting up AMPLS through the Azure portal, say for a proof of concept or to become familiar with the service, youâll find the experience quite straightforward. Itâs simple to set up the private link service and then add the private endpoints and DNS configuration required to use it. However, if you then start looking in to deploying this solution with Terraform, things become a bit more difficult. This article offers simple solutions to overcome the challenges youâll face.
Hurdle Number One: Provider Support
The first issue is that at the time of writing (July 2021) the Terraform Azure provider does not support AMPLS. The main reason for this is that the feature is not yet supported by the Azure Go SDK. Once the feature exists in the Go SDK it can then be added to the Terraform provider.
azurerm
provider here.This means weâll need to use good old ARM templates to deploy AMPLS, the tried and true fall-back when something is not yet supported by the azurerm
provider. If you havenât run in to this scenario before then youâll be pleased to know that the provider does offer a mechanism to deploy ARM templates, so you can still execute the deployment as part of your Terraform infrastructure code. Hereâs the code to do this.
resource "azurerm_resource_group_template_deployment" "this" {
name = "ampls"
resource_group_name = azurerm_resource_group.example.name
deployment_mode = "Incremental"
parameters_content = jsonencode({
"private_link_scope_name" = {
value = "ampls_private_scope"
}
"workspace_name" = {
value = azurerm_log_analytics_workspace.this.name
}
})
template_content = <<-EOT
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"private_link_scope_name": {
"defaultValue": "my-scope",
"type": "String"
},
"workspace_name": {
"defaultValue": "my-workspace",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "microsoft.insights/privatelinkscopes",
"apiVersion": "2019-10-17-preview",
"name": "[parameters('private_link_scope_name')]",
"location": "global",
"properties": {}
},
{
"type": "microsoft.insights/privatelinkscopes/scopedresources",
"apiVersion": "2019-10-17-preview",
"name": "[concat(parameters('private_link_scope_name'), '/', concat(parameters('workspace_name'), '-connection'))]",
"dependsOn": [
"[resourceId('microsoft.insights/privatelinkscopes', parameters('private_link_scope_name'))]"
],
"properties": {
"linkedResourceId": "[resourceId('microsoft.operationalinsights/workspaces', parameters('workspace_name'))]"
}
}
],
"outputs": {
"resourceID": {
"type": "String",
"value": "[resourceId('microsoft.insights/privatelinkscopes', parameters('private_link_scope_name'))]"
}
}
}
EOT
}
Hurdle Number Two: Azure DNS Records
The second hurdle to be considered is DNS. There are two parts to this, the architectural problem and the infrastructure code problem. Weâre going to focus on the latter here, but just be aware the you should put careful consideration in to how you configure DNS for AMPLS in the context of your environment. The Microsoft documentation on AMPLS covers these potential gotchas well.
The infrastructure code problem we face when trying to deploy AMPLS with Terraform is around replicating the outcome of deploying the service from the Azure portal. If you use the portal and create a private endpoint from the private link scope resource, the portal will offer an option to deploy the Azure Private DNS zones for you.
![](https://lachlanwright.com/content/images/2024/05/manual_create_dns_auto.png)
When you are creating the private endpoint and linking it to the private link scope in Terraform (or any infrastructure as code) this wonât be done automatically for you. Youâll need to create the DNS zones and A records yourself. The important thing to note here is that for the private endpoint to work properly you will need to assign particular IPs for each FQDN. The private endpoint will list the IP to be assigned to each FQDN if you take a look in the portal.
![](https://lachlanwright.com/content/images/2024/05/manual_pe_dns_configuration_not_done.png)
Whatâs happening here is that IPs from the private endpointâs subnet are being allocated to each FQDN in a certain order. If we replicate that order weâll be able to dynamically assign IPs from any give subnet in the same way the service would have if deployed via the portal. That means our AMPLS will work as expected and weâll be able to deploy using automation and infrastructure as code.
Weâll need to use the Terraform cidrhost
function which returns the IP address for a given host number within a subnet. So for example if we want the first host IP in the subnet 192.168.0.0/24
:
cidrhost("192.168.0.0/24", 1)
From here we just need to know what order the IPs need to be allocated in to replicate the outcome of deploying via the portal so that each IP is assigned to the correct FQDN. Keep in mind that the first four IP addresses from every subnet are reserved in Azure. Youâll also need the workspace ID of the log analytics workspace you are sending your logs to. Note that this is the workspace ID which is different to the resource ID. With that in mind, this is the order in which the IPs need to be allocated.
Number | FQDN |
---|---|
4 | <workspace id>.privatelink.oms.opinsights.azure.com |
5 | <workspace id>.privatelink.ods.opinsights.azure.com |
6 | <workspace id>.privatelink.agentsvc.azure-automation.net |
7 | api.privatelink.monitor.azure.com |
8 | global.in.ai.privatelink.monitor.azure.com |
9 | profiler.privatelink.monitor.azure.com |
10 | live.privatelink.monitor.azure.com |
11 | snapshot.privatelink.monitor.azure.com |
12 | scadvisorcontentpl.privatelink.blob.core.windows.net |
With that resolved youâll either need to assign the private DNS zones to any VNets that need to consume them, or update your DNS resolvers to forward requests for these zones to Azure DNS depending on your environment.
Conclusion
Now youâre ready to leverage the benefits of keeping all your monitoring traffic on private links and deploy it all using Terraform infrastructure as code. Weâve dealt with the lack of provider support with an ARM template deployment and resolved the DNS allocation issue as well. With the technical issues dealt with hopefully this frees you up to put more thought in to how AMPLS will fit in to your environment.