ansible-collections / community.general

Ansible Community General Collection
https://galaxy.ansible.com/ui/repo/published/community/general/
GNU General Public License v3.0
787 stars 1.45k forks source link

Cloudflare DNS: Bad Request on MX update #8418

Open aquarion opened 1 month ago

aquarion commented 1 month ago

Summary

When attempting an MX update with cloudflare dns, I get "400 Bad Request"

Issue Type

Bug Report

Component Name

cloudflare_dns

Ansible Version

ansible [core 2.16.7]
  config file = /home/aquarion/code/autopelago/ansible.cfg
  configured module search path = ['/home/aquarion/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/aquarion/code/autopelago/.direnv/python-venv-3.10.12/lib/python3.10/site-packages/ansible
  ansible collection location = /home/aquarion/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/aquarion/code/autopelago/.direnv/python-venv-3.10.12/bin/ansible
  python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/home/aquarion/code/autopelago/.direnv/python-venv-3.10.12/bin/python3)
  jinja version = 3.1.4
  libyaml = True

Community.general Version

aquarion@Cyclone ~/code/autopelago (main *): ansible-galaxy collection list community.general

# /home/aquarion/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 7.1.0  

# /home/aquarion/code/autopelago/.direnv/python-venv-3.10.12/lib/python3.10/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 8.3.0  

Configuration

CONFIG_FILE() = /home/aquarion/code/autopelago/ansible.cfg
DEFAULT_HOST_LIST(/home/aquarion/code/autopelago/ansible.cfg) = ['/home/aquarion/code/autopelago/etc/inventory.ini']
DEFAULT_ROLES_PATH(/home/aquarion/code/autopelago/ansible.cfg) = ['/home/aquarion/code/autopelago/galaxy-roles']
DEFAULT_VAULT_PASSWORD_FILE(/home/aquarion/code/autopelago/ansible.cfg) = /home/aquarion/.vault_pass.txt
EDITOR(env: EDITOR) = vim
INTERPRETER_PYTHON(env: ANSIBLE_PYTHON_INTERPRETER) = /home/aquarion/code/autopelago/.direnv/python-venv-3.10.12/bin/python

OS / Environment

Ubuntu 22.04

Steps to Reproduce

The first task works, the second does not.


    - name: (CF) Aquarionics.com. - AAAA
      community.general.cloudflare_dns:
        state: present
        zone: aquarionics.com
        record: "@"
        api_token: "{{ cloudflare_api_key }}"
        type: AAAA
        ttl: "300"
        value: "{{ ansible_default_ipv6.address }}"

    - name: (CF) Aquarionics.com. - MX 1/5
      community.general.cloudflare_dns:
        state: present
        zone: aquarionics.com
        api_token: "{{ cloudflare_api_key }}"
        type: MX
        ttl: 3600
        value: "ASPMX.L.GOOGLE.COM."

Expected Results

The playbook task should execute successfully

Actual Results

TASK [aqcom-cloudflare : (CF) Aquarionics.com. - AAAA] ******************************************************************************************************************************************
ok: [firth.water.gkhs.net]

TASK [aqcom-cloudflare : (CF) Aquarionics.com. - MX 1/5] ****************************************************************************************************************************************
fatal: [firth.water.gkhs.net]: FAILED! => {"changed": false, "msg": "API bad request; Status: 400; Method: POST: Call: /zones/********/dns_records;\n Data: {\"type\": \"MX\", \"name\": \"aquarionics.com\", \"content\": \"aspmx.l.google.com\", \"priority\": 1, \"ttl\": 3600}; Headers: {'Authorization': 'Bearer ********', 'Content-Type': 'application/json'}"}

I have made a minor change to cloudflare_dns.py to output the data & headers in the failure output, running the same API call with curl is unsuccessfuly successful, thus:


aquarion@Cyclone ~/code/autopelago (main *): 
curl --request POST
   --url https://api.cloudflare.com/client/v4/zones/******/dns_records
   --header 'Content-Type: application/json'
   --header 'Authorization: Bearer ******'
   --data '{"type": "MX", "name": "aquarionics.com", "content": "aspmx.l.google.com", "priority": 1, "ttl": 300}'

{"result":null,"success":false,"errors":[{"code":81058,"message":"A record with the same settings already exists."}],"messages":[]}

Code of Conduct

ansibullbot commented 1 month ago

Files identified in the description:

If these files are incorrect, please update the component name section of the description or use the !component bot command.

click here for bot help

ansibullbot commented 1 month ago

cc @mgruener click here for bot help

markverg commented 1 month ago

I just ran into this issue as well and was about the create an issue, so can confirm. First time it works fine, when trying to update it gives an error.

Just to add; when solo is set to true, there is no error it will overwrite each existing record. Setting solo to true doesn't work for MX records when there's multiple records with same name but different priorities just like the Google example @aquarion mentioned.

markverg commented 1 month ago

Can also confirm issue is also in newest version of community.general

# /root/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 9.0.0

# /usr/local/lib/python3.10/dist-packages/ansible_collections
Collection        Version
----------------- -------
community.general 5.8.3
radiozradioz commented 1 day ago

When I got this, I had used account_api_key instead of api_token to specify my API token. Changing the variable to api_token fixed the issue.

A generic 400 made me think it wasn't an authentication failure, rather something wrong in my request. Perhaps this is a case of the "bad request" response taking precedence over the "authentication failure" response.

I'm unsure if CloudFlare responded with any messages that could have hinted that the authentication details were provided incorrectly, as I cannot see the raw HTTP responses. Perhaps it would be a good idea for this, and similar, roles to print the raw HTTP request/response when under the highest level of verbosity.