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

What-If for Private Endpoint with DNS Zone Config Triggers Constant Changes #381

Open
trylvis opened this issue Nov 5, 2024 · 1 comment

Comments

@trylvis
Copy link

trylvis commented Nov 5, 2024

Bicep version
Bicep CLI version 0.30.23 (ec3612efc7)

Describe the bug
Deploying Private Endpoint with Private DNS Zone Config, always triggers changes running "what-if".

The changes are as follows, with an new etag each time;

  ~ Microsoft.Network/privateEndpoints/devxregistry001/privateDnsZoneGroups/pdzg-devxregistry001 [2023-11-01]
    ~ properties.privateDnsZoneConfigs: [
      ~ 0:

        - etag:                         "W/"8b50bdb8-d00f-4246-a042-f07211e47644""
        - id:                           "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/privateEndpoints/devxregistry001/privateDnsZoneGroups/pdzg-devxregistry001/privateDnsZoneConfigs/privatelink.azurecr.io"
        - properties.provisioningState: "Succeeded"
        - type:                         "Microsoft.Network/privateEndpoints/privateDnsZoneGroups/privateDnsZoneConfigs"

      ]

  = Microsoft.App/managedEnvironments/cae-base-hub-neu-01 [2024-03-01]
  = Microsoft.ContainerRegistry/registries/devxregistry001 [2023-06-01-preview]
  * Microsoft.Network/networkInterfaces/devxregistry001.nic.88fa9261-95a3-40bb-87f1-bc7c823290ac

To Reproduce

Private DNS Zone Group module:

// =============== //
//   Parameters    //
// =============== //

@description('Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment.')
param privateEndpointName string

@description('Required. Configuration of the private DNS zone for the private DNS zone group.')
param privateDnsZoneConfig privateDnsZoneGroupConfigType

@description('Optional. The name of the private DNS zone group.')
param name string = 'default'

// =============== //
//   Variables     //
// =============== //

var privateDnsZoneConfigVar = {
  name: privateDnsZoneConfig.?name ?? last(split(privateDnsZoneConfig.privateDnsZoneResourceId, '/'))
  properties: {
    privateDnsZoneId: privateDnsZoneConfig.privateDnsZoneResourceId
  }
}

// =============== //
//   Resources     //
// =============== //

resource privateEndpoint 'Microsoft.Network/privateEndpoints@2024-01-01' existing = {
  name: privateEndpointName
}

resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-11-01' = {
  name: name
  parent: privateEndpoint
  properties: {
    privateDnsZoneConfigs: [ privateDnsZoneConfigVar ] // Wrapping in array to satisfy Azure resource requirements
  }
}

// =============== //
//   Outputs       //
// =============== //

@description('The name of the private endpoint DNS zone group.')
output name string = privateDnsZoneGroup.name

@description('The resource ID of the private endpoint DNS zone group.')
output resourceId string = privateDnsZoneGroup.id

@description('The resource group the private endpoint DNS zone group was deployed into.')
output resourceGroupName string = resourceGroup().name

// =============== //
//   Types         //
// =============== //

@export()
type privateDnsZoneGroupConfigType = {
  @description('Optional. The name of the private DNS zone config.')
  name: string?

  @description('Required. The resource id of the private DNS zone.')
  privateDnsZoneResourceId: string
}

Private Endpoint Module:

// =============== //
//   Imports       //
// =============== //

import { privateDnsZoneGroupConfigType } from 'private-dns-zone-group/main.bicep'

// =============== //
//   Metadata      //
// =============== //

metadata name = 'Private Endpoints'
metadata description = 'This module deploys a Private Endpoint.'

// =============== //
//   Parameters    //
// =============== //

@description('Required. The configuration for the private endpoint.')
param privateEndpoint privateEndpointType

// =============== //
//   Resources     //
// =============== //

resource privateEndpointResource 'Microsoft.Network/privateEndpoints@2024-01-01' = {
  name: privateEndpoint.name
  location: privateEndpoint.location ?? resourceGroup().location
  properties: {
    subnet: {
      id: privateEndpoint.subnetId
    }
    privateLinkServiceConnections: [
      {
        name: privateEndpoint.name
        properties: {
          privateLinkServiceId: privateEndpoint.targetResourceId
          groupIds: [privateEndpoint.targetResourceType]
        }
      }
    ]
  }
}

// Private DNS Zone Group - linking Private Endpoint to Private DNS Zone
module privateEndpoint_privateDnsZoneGroup 'private-dns-zone-group/main.bicep' = if (!empty(privateEndpoint.privateDnsZoneGroup)) {
  name: '${privateEndpoint.name}-PrivateEndpoint-PrivateDnsZoneGroup'
  params: {
    name: 'pdzg-${privateEndpoint.name}'
    privateEndpointName: privateEndpoint.name
    privateDnsZoneConfig: privateEndpoint.privateDnsZoneGroup!
  }
}

// =============== //
//   Outputs       //
// =============== //

@description('The resource ID of the private endpoint.')
output privateEndpointId string = privateEndpointResource.id

@description('The name of the private endpoint.')
output privateEndpointName string = privateEndpointResource.name

// =============== //
//   Types         //
// =============== //

@export()
type privateEndpointType = {
  @description('Required. The name of the private endpoint.')
  name: string

  @description('Optional. The location where the private endpoint is deployed.')
  location: string?

  @description('Required. The resource ID of the subnet where the private endpoint needs to be created.')
  subnetId: string

  @description('Required. The resource ID of the target resource (e.g., storage account, SQL server) to which the private endpoint connects.')
  targetResourceId: string

  @description('Required. The sub-resource type (e.g., "blob" for storage accounts).')
  targetResourceType: string

  @description('Optional. The Private DNS Zone configuration for the Private Endpoint.')
  privateDnsZoneGroup: privateDnsZoneGroupConfigType? 
}

@export()
type privateEndpointConfig = {
  @description('Required. The resource ID of the subnet where the private endpoint will be created.')
  subnetId: string

  @description('Optional. The resource ID of the private DNS zone to associate with the private endpoint.')
  privateDnsZoneResourceId: string?
}

And it is then used on various resources like ACR like this;

module privateEndpointModules '../network/private-endpoint/main.bicep' = [
  for (privateEndpoint, i) in (containerRegistry.privateEndpoints ?? []): {
    name: 'acrPrivateEndpoint-${i}'
    params: {
      privateEndpoint: {
        name: registry.name
        location: location
        targetResourceType: 'registry'
        targetResourceId: registry.id
        subnetId: privateEndpoint.subnetId
        privateDnsZoneGroup: {
          privateDnsZoneResourceId: privateEndpoint.privateDnsZoneResourceId
        }
      }
    }
  }
]

Additional context

Is there any way to avoid this? I would like to be able to run what-if without any it printing any changes - unless some has actually been made.

Firstly it is annoying but it would also be handy to be able to use what-if in some "diff" check.

Thanks!

@slavizh
Copy link

slavizh commented Nov 5, 2024

What-if noise issues can be reported here: https://aka.ms/WhatIfIssues but overall it is useless as these issues can only be fixed by teams developing the resource providers and they are not fixing those. Bicep team cannot do anything about noise issues without coming out with a completely new what-if architecture.

@alex-frankel alex-frankel transferred this issue from Azure/bicep Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Todo
Development

No branches or pull requests

2 participants