Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for azurerm_dashboard_grafana managed private endpoints #23950

Open
1 task done
djryanj opened this issue Nov 17, 2023 · 9 comments
Open
1 task done

Support for azurerm_dashboard_grafana managed private endpoints #23950

djryanj opened this issue Nov 17, 2023 · 9 comments

Comments

@djryanj
Copy link
Contributor

djryanj commented Nov 17, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment and review the contribution guide to help.

Description

Currently, there is no support for a managed private endpoint in a managed Grafana. This resource is required to enable private communications between e.g., Grafana and an Azure Monitor Workspace. Note that this is in preview.

New or Affected Resource(s)/Data Source(s)

azurerm_dashboard_grafana_managed_private_endpoint

Potential Terraform Configuration

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "West Europe"
}

resource "azurerm_monitor_workspace" "example" {
  name                = "example-mamw"
  resource_group_name = azurerm_resource_group.example.name
  location            = "West Europe"
  tags = {
    key = "value"
  }
}

resource "azurerm_dashboard_grafana" "example" {
  name                              = "example-dg"
  resource_group_name               = azurerm_resource_group.example.name
  location                          = "West Europe"
  api_key_enabled                   = true
  deterministic_outbound_ip_enabled = true
  public_network_access_enabled     = false

  identity {
    type = "SystemAssigned"
  }

  tags = {
    key = "value"
  }
}

resource "azurerm_dashboard_grafana_managed_private_endpoint" "example" {
  name                  = "example-dg-mpe"
  dashboard_grafana_id  = azurerm_dashboard_grafana.example.id
  target_resource_id    = azurerm_monitor_workspace.example.id
  subresource_name      = "prometheusMetrics"
}

References

@neil-yechenwei
Copy link
Contributor

Thanks for raising this issue. I assume that Terraform is using the common resource "azurerm_private_endpoint" for this scenario so that TF has supported private endpoint with dashboard grafana. Please check whether it's as what you expect. Thanks.

@djryanj
Copy link
Contributor Author

djryanj commented Nov 20, 2023

@neil-yechenwei they aren't the same thing. A private endpoint is about accessing the resource from within your virtual network, a managed private endpoint is about giving the resource access to things inside your virtual network. For example, this managed Grafana instance needs a managed private endpoint to be able to connect to, say, an Azure Monitor workspace with a private link that lives in your virtual network. I don't see that functionality supported by azurerm_private_endpoint. Said another way, a private endpoint is about inbound access, a managed private endpoint is about outbound.

See: https://learn.microsoft.com/en-us/azure/managed-grafana/how-to-connect-to-data-source-privately

And: https://learn.microsoft.com/en-us/rest/api/managed-grafana/managed-private-endpoints/create?view=rest-managed-grafana-2023-09-01&tabs=HTTP

And: https://pkg.go.dev/github.com/hashicorp/go-azure-sdk/resource-manager/dashboard/2023-09-01/managedprivateendpoints

Data Factory also has a managed private endpoint: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/data_factory_managed_private_endpoint

@yarashagarwal
Copy link

Any update on this issue?

@yavuzkaymak
Copy link

AzApi, Bicep examples can be found here in learn.microsoft

@djryanj
Copy link
Contributor Author

djryanj commented Apr 3, 2024

@yavuzkaymak that's what I'm doing for now, but it's extremely non-intuitive to get right, especially since you need a few different AzApi resources to get the full end-to-end experience; e.g., approving the endpoint. Here's some example code that's not quite complete (e.g., missing resource group definitions but they're referenced):

resource "azurerm_dashboard_grafana" "this" {
  name                              = "grafana"
  resource_group_name               = azurerm_resource_group.this.name
  location                          = azurerm_resource_group.this.location
  api_key_enabled                   = false
  deterministic_outbound_ip_enabled = true
  public_network_access_enabled     = false
  tags                              = azurerm_resource_group.this.tags
  grafana_major_version             = "10"
  identity {
    type = "SystemAssigned"
  }

  azure_monitor_workspace_integrations {
    resource_id = azurerm_monitor_workspace.this.id
  }
}

resource "azapi_resource" "grafana_managed_private_endpoint_connection" {
  type      = "Microsoft.Dashboard/grafana/managedPrivateEndpoints@2022-10-01-preview"
  name      = azurerm_monitor_workspace.this.name
  tags      = azurerm_resource_group.this.tags
  location  = azurerm_resource_group.this.location
  parent_id = azurerm_dashboard_grafana.this.id
  body = jsonencode({
    properties = {
      groupIds = [
        "prometheusMetrics"
      ]
      privateLinkResourceId     = azurerm_monitor_workspace.this.id
      privateLinkResourceRegion = azurerm_resource_group.this.location
      requestMessage            = "Created by Terraform"
    }
  })
}

# the actual azurerm_monitor_workspace resource doesn't yet export the private endpoint connections information
# so we use the azapi provider to get that (once they've been created, otherwise things fail)
data "azapi_resource" "azurerm_monitor_workspace" {
  type                   = "Microsoft.Monitor/accounts@2023-04-03"
  resource_id            = azurerm_monitor_workspace.this.id
  response_export_values = ["properties.privateEndpointConnections"]
  depends_on             = [azapi_resource.grafana_managed_private_endpoint_connection]
}

# Retrieve the private endpoint connection name from the monitor account based on the private endpoint name
locals {
  private_endpoint_connection_name = element([
    for connection in jsondecode(data.azapi_resource.azurerm_monitor_workspace.output).properties.privateEndpointConnections
    : connection.name
    if endswith(connection.properties.privateEndpoint.id, "grafana-grafana-${azurerm_monitor_workspace.this.name}")
  ]
}

# Approve the managed private endpoints - this is rather hacky, but remarkably it works since the azurerm
# provider doesn't have the ability to do this natively yet 
resource "azapi_update_resource" "grafana_managed_private_endpoint_connection_approval" {
  type      = "Microsoft.Monitor/accounts/privateEndpointConnections@2023-04-03"
  name      = local.private_endpoint_connection_name
  parent_id = azurerm_monitor_workspace.this.id

  body = jsonencode({
    properties = {
      privateLinkServiceConnectionState = {
        actionsRequired = "None"
        description     = "Approved via Terraform"
        status          = "Approved"
      }
    }
  })
  depends_on = [azapi_resource.grafana_managed_private_endpoint_connection]
}

@yavuzkaymak
Copy link

@yavuzkaymak that's what I'm doing for now, but it's extremely non-intuitive to get right, especially since you need a few different AzApi resources to get the full end-to-end experience; e.g., approving the endpoint. Here's some example code that's not quite complete (e.g., missing resource group definitions but they're referenced):

resource "azurerm_dashboard_grafana" "this" {
  name                              = "grafana"
  resource_group_name               = azurerm_resource_group.this.name
  location                          = azurerm_resource_group.this.location
  api_key_enabled                   = false
  deterministic_outbound_ip_enabled = true
  public_network_access_enabled     = false
  tags                              = azurerm_resource_group.this.tags
  grafana_major_version             = "10"
  identity {
    type = "SystemAssigned"
  }

  azure_monitor_workspace_integrations {
    resource_id = azurerm_monitor_workspace.this.id
  }
}

resource "azapi_resource" "grafana_managed_private_endpoint_connection" {
  type      = "Microsoft.Dashboard/grafana/managedPrivateEndpoints@2022-10-01-preview"
  name      = azurerm_monitor_workspace.this.name
  tags      = azurerm_resource_group.this.tags
  location  = azurerm_resource_group.this.location
  parent_id = azurerm_dashboard_grafana.this.id
  body = jsonencode({
    properties = {
      groupIds = [
        "prometheusMetrics"
      ]
      privateLinkResourceId     = azurerm_monitor_workspace.this.id
      privateLinkResourceRegion = azurerm_resource_group.this.location
      requestMessage            = "Created by Terraform"
    }
  })
}

# the actual azurerm_monitor_workspace resource doesn't yet export the private endpoint connections information
# so we use the azapi provider to get that (once they've been created, otherwise things fail)
data "azapi_resource" "azurerm_monitor_workspace" {
  type                   = "Microsoft.Monitor/accounts@2023-04-03"
  resource_id            = azurerm_monitor_workspace.this.id
  response_export_values = ["properties.privateEndpointConnections"]
  depends_on             = [azapi_resource.grafana_managed_private_endpoint_connection]
}

# Retrieve the private endpoint connection name from the monitor account based on the private endpoint name
locals {
  private_endpoint_connection_name = element([
    for connection in jsondecode(data.azapi_resource.azurerm_monitor_workspace.output).properties.privateEndpointConnections
    : connection.name
    if endswith(connection.properties.privateEndpoint.id, "grafana-grafana-${azurerm_monitor_workspace.this.name}")
  ]
}

# Approve the managed private endpoints - this is rather hacky, but remarkably it works since the azurerm
# provider doesn't have the ability to do this natively yet 
resource "azapi_update_resource" "grafana_managed_private_endpoint_connection_approval" {
  type      = "Microsoft.Monitor/accounts/privateEndpointConnections@2023-04-03"
  name      = local.private_endpoint_connection_name
  parent_id = azurerm_monitor_workspace.this.id

  body = jsonencode({
    properties = {
      privateLinkServiceConnectionState = {
        actionsRequired = "None"
        description     = "Approved via Terraform"
        status          = "Approved"
      }
    }
  })
  depends_on = [azapi_resource.grafana_managed_private_endpoint_connection]
}

Totally agree. I just wanted to once more highlight that it requires a separate resource type and not possible with azurerm's private endpoint.

@AxelTob
Copy link

AxelTob commented Apr 24, 2024

I am struggling with this myself. I did not get your solution above to work. Have you found another solution for this since creating the issue?

I don't necessary need the grafana to be private but I want the workplace and collection_endpoint to have public_network_access_enabled = false
djryanj

@djryanj
Copy link
Contributor Author

djryanj commented Apr 30, 2024

@AxelTob not via Terraform, no. You can obviously achieve this manually, but the only way I've found is using the above.

@AndrewCi
Copy link

Still looking for this feature. Looks like it's available for other Azure services https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/data_factory_managed_private_endpoint in the TF resource provider

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants