ansible-collections / azure

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

azure_rm_adapplication is not idempotent #1573

Closed mucsi96 closed 2 weeks ago

mucsi96 commented 1 month ago
SUMMARY

azure_rm_adapplication is not idempotent. It creates new application every time the module is executed.

Screenshot 2024-05-23 at 19 26 42
ISSUE TYPE
COMPONENT NAME

azure_rm_adapplication

ANSIBLE VERSION
ansible [core 2.16.6]
  config file = None
  configured module search path = ['/Users/igorbari/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/igorbari/.pyenv/versions/3.12.3/envs/aks-roles/lib/python3.12/site-packages/ansible
  ansible collection location = /Users/igorbari/.ansible/collections:/usr/share/ansible/collections
  executable location = /Users/igorbari/.pyenv/versions/3.12.3/envs/aks-roles/bin/ansible
  python version = 3.12.3 (main, May 10 2024, 20:52:14) [Clang 15.0.0 (clang-1500.1.0.2.5)] (/Users/igorbari/.pyenv/versions/3.12.3/envs/aks-roles/bin/python3.12)
  jinja version = 3.1.4
  libyaml = True
COLLECTION VERSION

# /Users/igorbari/.pyenv/versions/3.12.3/envs/aks-roles/lib/python3.12/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 8.6.0  
CONFIGURATION
CONFIG_FILE() = None
OS / ENVIRONMENT

MacBook Air Apple M2 macOS Somona

STEPS TO REPRODUCE
- name: Create identity provider application
  delegate_to: localhost
  azure.azcollection.azure_rm_adapplication:
    tenant: "{{ azure_tenant_id }}"
    display_name: identity-app
EXPECTED RESULTS

The application should be created only once

ACTUAL RESULTS

New application is created on every playbook execution

Screenshot 2024-05-23 at 19 26 42
Executing task: bash scripts/run_playbook.sh playbooks/create.yml 

PLAY [Create Azure Kubernetes Cluster] ***************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************
ok: [localhost]

TASK [setup_identity_provider : Validating arguments against arg spec 'main'] ************************************************************************************
ok: [localhost]

TASK [setup_identity_provider : Get account info] ****************************************************************************************************************
ok: [localhost]

TASK [setup_identity_provider : set_fact] ************************************************************************************************************************
ok: [localhost]

TASK [setup_identity_provider : Create dns challenge application] ************************************************************************************************
changed: [localhost]

TASK [setup_identity_provider : Get dns challenge application info] **********************************************************************************************
ok: [localhost]

PLAY RECAP *******************************************************************************************************************************************************
localhost                  : ok=6    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
Fred-sun commented 1 month ago

@mucsi96 I am very sorry for the trouble brought to you, because this module is special and there is an app with the same name. Therefore, if the script is executed again, please bring the app_id corresponding to the app, which will be idempotent or update the current app. Thank you!

mucsi96 commented 1 month ago

Not sure I fully understand. Can you please give me a full example how can I use this module in declarative way. With idempotency in mind.

I might get it wrong. But if I need to provide different parameters depending if app already exisst or not it means this module is not idempotent. The module should be able to check if desired state is already achived by checking if application with given name already exists. This is the core concepts of Ansible. https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_intro.html#desired-state-and-idempotency

This is how most of Azure collection modules work as well.

Right now I am using this module in following way.

- name: List all applications
  delegate_to: localhost
  azure.azcollection.azure_rm_adapplication_info:
    tenant: "{{ azure_tenant_id }}"
  register: azure_ad_apps

- name: Find identity provider application
  set_fact:
    azure_identity_app: "{{ item }}"
  loop: "{{ azure_ad_apps.applications }}"
  when: item.app_display_name == 'identity-app'

- name: Create identity provider application
  delegate_to: localhost
  azure.azcollection.azure_rm_adapplication:
    tenant: "{{ azure_tenant_id }}"
    display_name: identity-app
  register: azure_identity_app
  when: azure_identity_app is not defined

- name: List all applications again
  delegate_to: localhost
  azure.azcollection.azure_rm_adapplication_info:
    tenant: "{{ azure_tenant_id }}"
  register: azure_ad_apps

- name: Find identity provider application again
  set_fact:
    azure_identity_app: "{{ item }}"
  loop: "{{ azure_ad_apps.applications }}"
  when: item.app_display_name == 'identity-app'

- debug:
    var: azure_identity_app

As you can see it requires a huge boilerplate. Only this way I can achieve idempotency right now and getting the same output independentyl if it's running for the first time or nth time.

Fred-sun commented 1 month ago

@mucsi96 You can add app_id to avoid duplicate creation when duplicate creation is not required. Thank you!


- name: Create identity provider application --- First crated
  delegate_to: localhost
  azure.azcollection.azure_rm_adapplication:
    tenant: "{{ azure_tenant_id }}"
    display_name: identity-app
  register: azure_identity_app
- name: Create identity provider application --- Secondary create, Add app_id parameter to idempotent test
  delegate_to: localhost
  azure.azcollection.azure_rm_adapplication:
    tenant: "{{ azure_tenant_id }}"
    display_name: identity-app
    app_id: "{{ azure_identity_app.app_id }}"
  register: azure_identity_app

Results:

TASK [Create identity provider application --- First crated] ***********************************************************
changed: [localhost]

TASK [Create identity provider application --- Secondary create, Add app_id parameter to idempotent test] **************
ok: [localhost]