ansible-collections / azure

Development area for Azure Collections
https://galaxy.ansible.com/azure/azcollection
GNU General Public License v3.0
245 stars 327 forks source link

azure_rm_privatednszonelink - vNet subscription_id being overridden by value from profile #1021

Closed fdalrymple-hp closed 1 year ago

fdalrymple-hp commented 1 year ago
SUMMARY

When executing module, supplying entire resource ID to the "virtual_network" parameter, seeing unexpected behavior. Ansible module seems to be reading the information provided to it correctly, however reviewing Azure logs would indicate that the wrong subscription ID is being provided to the call to ARM. The correct name and resource groups are being parsed and used.

ISSUE TYPE
COMPONENT NAME

azure_rm_privatednszonelink

ANSIBLE VERSION
ansible [core 2.13.6]
  config file = None
  configured module search path = ['/home/username/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.9/dist-packages/ansible
  ansible collection location = /home/username/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110]
  jinja version = 3.1.2
  libyaml = True
COLLECTION VERSION
# /home/username/.ansible/collections/ansible_collections
Collection         Version
------------------ -------
azure.azcollection 1.14.0

# /usr/local/lib/python3.9/dist-packages/ansible_collections
Collection         Version
------------------ -------
azure.azcollection 1.13.0
CONFIGURATION
<empty>
OS / ENVIRONMENT

username@hostname:~$ cat /etc/debian_version 11.5

STEPS TO REPRODUCE
---
- hosts: all
  connection: local
  vars:
    core_subscription_id: "55555555-4444-3333-2222-111111111111"
    vnets:
    - name: vnet-1
      region: eastus
      subscription: "11111111-2222-3333-4444-555555555555"
      resource_group: rg-networking
      dnsvnetlinks:
        - zone_name: zone.example.com
          registration_enabled: yes
          resource_group: rg-networking
  - name: Add Private DNS Zone vnet links
    azure.azcollection.azure_rm_privatednszonelink:
      name: "dnsvnetlink-{{ item.0['name']|regex_replace('^vnet-', '') }}"
      profile: core
      registration_enabled: "{{ item.1['registration_enabled'] }}"
      resource_group: "{{ item.1['resource_group'] }}"
      subscription_id: "{{ core_subscription_id }}"
      virtual_network: "/subscriptions/{{ item.0['subscription'] }}/resourceGroups/{{ item.0['resource_group'] }}/providers/Microsoft.Network/virtualNetworks/{{ item.0['name'] }}"
      zone_name: "{{ item.1['zone_name'] }}"
    loop: "{{ vnets|subelements('dnsvnetlinks') }}"
EXPECTED RESULTS

Expect the vnet link to be created, or if it already exists for it to indicate so and not make any changes.

ACTUAL RESULTS

In the command output I get an error as seen below. Also worth noting that in the verbose output (invocation section) I see the vNet with the correct ID. In the Azure portal activity logs I get "message": "Virtual network resource not found for '/subscriptions/55555555-4444-3333-2222-111111111111/resourceGroups/rg-networking/providers/Microsoft.Network/virtualNetworks/vnet-1'". The subscription_id for the vNet is the one related to the profile being used in the task, NOT the one provided in the virtual_network parameter nor the one showing in the verbose ansible-playbook output. The text error in the output from ansible-playbook would indicate an operational conflict, however that error message is less deterministic than the indication from Azure Resource Manager that an incorrect resource ID is being used.

<azure-prod> ESTABLISH LOCAL CONNECTION FOR USER: username
<azure-prod> EXEC /bin/sh -c 'echo ~username && sleep 0'
<azure-prod> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/username/.ansible/tmp `"&& mkdir "` echo /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601 `" && echo ansible-tmp-1668013990.5085132-7259-92530896260601="` echo /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601 `" ) && sleep 0'
Using module file /home/username/.ansible/collections/ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednszonelink.py
<azure-prod> PUT /home/username/.ansible/tmp/ansible-local-7040joyl_665/tmp9usjihry TO /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601/AnsiballZ_azure_rm_privatednszonelink.py
<azure-prod> EXEC /bin/sh -c 'chmod u+x /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601/ /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601/AnsiballZ_azure_rm_privatednszonelink.py && sleep 0'
<azure-prod> EXEC /bin/sh -c '/usr/bin/python3 /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601/AnsiballZ_azure_rm_privatednszonelink.py && sleep 0'
<azure-prod> EXEC /bin/sh -c 'rm -f -r /home/username/.ansible/tmp/ansible-tmp-1668013990.5085132-7259-92530896260601/ > /dev/null 2>&1 && sleep 0'
The full traceback is:
  File "/tmp/ansible_azure.azcollection.azure_rm_privatednszonelink_payload_n7i7562_/ansible_azure.azcollection.azure_rm_privatednszonelink_payload.zip/ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednszonelink.py", line 291, in create_or_update_network_link
  File "/usr/local/lib/python3.9/dist-packages/azure/mgmt/privatedns/operations/_virtual_network_links_operations.py", line 163, in begin_create_or_update
    raw_result = self._create_or_update_initial(
  File "/usr/local/lib/python3.9/dist-packages/azure/mgmt/privatedns/operations/_virtual_network_links_operations.py", line 101, in _create_or_update_initial
    map_error(status_code=response.status_code, response=response, error_map=error_map)
  File "/usr/local/lib/python3.9/dist-packages/azure/core/exceptions.py", line 107, in map_error
    raise error
[WARNING]: Azure API profile latest does not define an entry for PrivateDnsManagementClient
failed: [azure-prod] (item=[{'name': 'vnet-1', 'region': 'eastus', 'subscription': '11111111-2222-3333-4444-555555555555', 'resource_group': 'rg-networking', 'dnsvnetlinks': [{'zone_name': 'zone.example.com', 'registration_enabled': True, 'resource_group': 'rg-networking'}]}, {'zone_name': 'zone.example.com', 'registration_enabled': True}]) => {
    "ansible_loop_var": "item",
    "changed": false,
    "invocation": {
        "module_args": {
            "ad_user": null,
            "adfs_authority_url": null,
            "api_profile": "latest",
            "append_tags": true,
            "auth_source": "auto",
            "cert_validation_mode": null,
            "client_id": null,
            "cloud_environment": "AzureCloud",
            "log_mode": null,
            "log_path": null,
            "name": "dnsvnetlink-1",
            "password": null,
            "profile": "core",
            "registration_enabled": true,
            "resource_group": "rg-networking",
            "secret": null,
            "state": "present",
            "subscription_id": "55555555-4444-3333-2222-111111111111",
            "tags": null,
            "tenant": null,
            "thumbprint": null,
            "virtual_network": "/subscriptions/11111111-2222-3333-4444-555555555555/resourceGroups/rg-networking/providers/Microsoft.Network/virtualNetworks/vnet-1",
            "x509_certificate_path": null,
            "zone_name": "zone.example.com"
        }
    },
    "item": [
        {
            "dnsvnetlinks": [
                {
                    "registration_enabled": true,
                    "zone_name": "zone.example.com",
                    "resource_group": "rg-networking"
                }
            ],
            "name": "vnet-1",
            "resource_group": "rg-networking",
            "subscription": "11111111-2222-3333-4444-555555555555"
        },
        {
            "registration_enabled": true,
            "zone_name": "zone.example.com",
            "resource_group": "rg-networking"
        }
    ],
    "msg": "Error creating or updating virtual network link dnsvnetlink-1 - (Conflict) Operation group '/operations/groups/id/|virtualNetworkLinks|55555555-4444-3333-2222-111111111111|rg-networking|zone.example.com|dnsvnetlink-1' already has 1 operations like '/operations/type/UpsertVirtualNetworkLink/id/5c97da98-4765-4cf8-86f1-088ce764027b_650f74e6-1373-4e94-8eac-a63e81064d9b' queued.\nCode: Conflict\nMessage: Operation group '/operations/groups/id/|virtualNetworkLinks|55555555-4444-3333-2222-111111111111|rg-networking|zone.example.com|dnsvnetlink-1' already has 1 operations like '/operations/type/UpsertVirtualNetworkLink/id/5c97da98-4765-4cf8-86f1-088ce764027b_650f74e6-1373-4e94-8eac-a63e81064d9b' queued."
Fred-sun commented 1 year ago

@fdalrymple-hp Your virtual network must be under the same subscription ID as the resource you create. Thanks!

fdalrymple-hp commented 1 year ago

Will there be no intention to support a vNet not in the same subscription as the Private DNS Zone in the future? I suspect that our configuration is very typical of a normal enterprise with hub & spoke architecture and in this case for our main private DNS zone the only vNet that's in the same subscription is the core/transit vNet. All of the other vNets reside in more app-specific subscription outside of the core-connectivity subscription.

Additional data points:

Fred-sun commented 1 year ago

@fdalrymple-hp Currently we don't support vnet_id under different subscriptions, but we are considering adding this feature. Thanks!

michael-andretta commented 1 year ago

image I know this is an older thread but does support exist for same subscription ID but with different resource groups. Say Private DNS zone is in RG-DNS where the vnet being linked to the zone is in RG-NETWORK, is that supported?

Fred-sun commented 1 year ago

@michael-andretta Do you want create a virtual network link like this?


- name: Create a Private DNS zone
  azure_rm_privatednszone:
    resource_group: "{{ resource_group }}"
    name: "{{ domain_name }}.com"
    state: present

- name: Create a virtual network
  azure_rm_virtualnetwork:
    name: "vnet{{ rpfx }}"
    resource_group: "{{ resource_group_secondary }}"
    address_prefixes_cidr:
        - 10.1.0.0/16
        - 172.100.0.0/16
    dns_servers:
        - 127.0.0.1
        - 127.0.0.2
  register: vnet_output

- name: Create a subnet
  azure_rm_subnet:
    name: "subnet{{ rpfx }}"
    virtual_network_name: "vnet{{ rpfx }}"
    resource_group: "{{ resource_group_secondary }}"
    address_prefix_cidr: 10.1.0.0/24

- name: Create a virtual network link
  azure_rm_privatednszonelink:
    resource_group: "{{ resource_group }}"
    name: "{{ link_name }}"
    zone_name: "{{ domain_name }}.com"
    virtual_network: "{{ vnet_output.state.id }}"
    registration_enabled: true
    state: present
simjonzz commented 1 year ago

I've recently encountered the same issue and after some digging we seem to have tracked down what's seems to be causing it. From the looks if it this has always fallen back on the current subscription context.

Starting in the module where it is parsing the resource id: https://github.com/ansible-collections/azure/blob/ebce7484bff7387ccc627180088008ed39be4a8d/plugins/modules/azure_rm_privatednszonelink.py#L160:~:text=self.virtual_network%20%3D%20format_resource_id,virtual_network%5B%27resource_group%27%5D)

Noting: subscription_id=virtual_network['subscription_id']

Looking further into parse_resource_id: https://github.com/Azure/msrestazure-for-python/blob/8e3def8c9927a349596e575de93df76de7a3a2b6/msrestazure/tools.py#L115:~:text=match%20%3D%20_ARMID_RE.match(rid)

And then taking a look at the regex it's trying to match.

https://github.com/Azure/msrestazure-for-python/blob/8e3def8c9927a349596e575de93df76de7a3a2b6/msrestazure/tools.py#L34-L36:~:text=_ARMID_RE%20%3D%20re,P%3Cchildren%3E.*))%3F%27)

Here it's trying to match on "subscription" and not "subscription_id" which the module expects.

With a quick local test I got this to work by removing "_id" like this:

self.virtual_network = format_resource_id(val=virtual_network['name'], subscription_id=virtual_network['subscription'], namespace='Microsoft.Network', types='virtualNetworks', resource_group=virtual_network['resource_group'])

Currently haven't tested if this break anything else, but deploying a private dns zone link for a virtual network in another subscription works. It also picks up on the existing one on additional runs.

Tasks used to successfully deploy link for virtual network in different subscription after changing the function:

rnotley commented 1 year ago

@Fred-sun

Currently we don't support vnet_id under different subscriptions, but we are considering adding this feature. Thanks!

Please consider reopening this issue. I'm running into the same problem and would like support for vnet_id under a different subscription.

fabriceverkor commented 4 months ago

I confirm I have the problem with last following version : azure-cli 2.60.0-1~bookworm amd64 Azure CLI

and : /usr/lib/python3/dist-packages/ansible_collections Collection Version


azure.azcollection 1.14.0

/etc/ansible/collections/ansible_collections Collection Version


azure.azcollection 2.3.0

The error message is below. Even is provided virtual_network ID is correct (because it works when I use it to create link from Azure GUI), the error reports the bad virtual_network ID with the local subscription ID instead of remote subscription ID.

"ansible_loop_var": "item",
"changed": false,
"invocation": {
    "module_args": {
        "ad_user": null,
        "adfs_authority_url": null,
        "api_profile": "latest",
        "append_tags": true,
        "auth_source": "auto",
        "cert_validation_mode": null,
        "client_id": null,
        "cloud_environment": "AzureCloud",
        "disable_instance_discovery": false,
        "log_mode": null,
        "log_path": null,
        "name": "pp-dataplatform-network-vnet_To_privatelink.azurecr.io",
        "password": null,
        "profile": "default",
        "registration_enabled": false,
        "resource_group": "fortigate",
        "secret": null,
        "state": "present",
        "subscription_id": null,
        "tags": null,
        "tenant": null,
        "thumbprint": null,
        "virtual_network": "/subscriptions/<remote_subscription_id>/resourceGroups/pp-dataplatform-network-rg/providers/Microsoft.Network/virtualNetworks/pp-dataplatform-network-vnet",
        "x509_certificate_path": null,
        "zone_name": "privatelink.azurecr.io"
    }
},
"item": "Gre-Data-Platform-WestEurope-Preprod-Subscription",
"msg": "Error creating or updating virtual network link pp-dataplatform-network-vnet_To_privatelink.azurecr.io - (BadRequest) Virtual network resource not found for '/subscriptions/<local_subscription_id>/resourceGroups/pp-dataplatform-network-rg/providers/Microsoft.Network/virtualNetworks/pp-dataplatform-network-vnet'\nCode: BadRequest\nMessage: Virtual network resource not found for '/subscriptions/<local_subscription_id>/resourceGroups/pp-dataplatform-network-rg/providers/Microsoft.Network/virtualNetworks/pp-dataplatform-network-vnet'"

}

When I fix azure_rm_privatednszonelink.py as mentioned above, see code below : ... if self.virtual_network: virtual_network = self.parse_resource_to_dict(self.virtual_network) self.virtual_network = format_resource_id(val=virtual_network['name'], subscription_id=virtual_network['subscription'], namespace='Microsoft.Network', types='virtualNetworks', resource_group=virtual_network['resource_group'])

I get following error : ... KeyError: 'subscription' ... meaning that virtual_network dict has no 'subscription' key.

Thank you for help.