Passing password variable into API raw json giving an error and code failing

Hi,

I believe I am getting the syntax wrong for passing in a variable to an API, raw json body but googling on and off for days has not helped. Hopefully someone can point me in the right direction on here. The playbook is trying to add a new Dell server iDRAC into open manage enterprise (OME) via a Discovery job. The variable I am trying to pass into the json body is {{ dell_idrac_root_password }}. I have already tried wrapping the variable in quotation marks, single marks, no brackets at all etc etc, everyway I can think to present the variable.

Calling the playbook with extra-vars (this includes the password variable):

ansible-playbook R450_ome_deploy_server_from_template_test5.yml --extra-vars "dell_ome_server=delomelab.madeup.com dell_ome_user=username dell_ome_password='fakePassword' dell_ome_device_idrac_ip=10.20.254.10 dell_idrac_root_password='fakePassword'" -v

playbook code:

- name: Add server into OME
  hosts: localhost
  connection: local
  gather_facts: 'no'
  
  tasks:
    - set_fact:
        random: "{{ 9999999999999999 | random }}"
      run_once: yes

    - name: Add device into OME for fresh discovery
      uri:
        url: >-
          https://{{ dell_ome_server
          }}/api/DiscoveryConfigService/DiscoveryConfigGroups
        user: '{{ dell_ome_user }}@email.com'
        password: '{{ dell_ome_password }}'
        method: POST
        body_format: json
        body:
          DiscoveryConfigGroupName: Discovery-{{ random }}
          DiscoveryConfigGroupDescription: 'null'
          DiscoveryStatusEmailRecipient: email@email.com
          DiscoveryConfigModels:
            - DiscoveryConfigId: 331105536
              DiscoveryConfigDescription: ''
              DiscoveryConfigStatus: ''
              DiscoveryConfigTargets:
                - DiscoveryConfigTargetId: 0
                  NetworkAddressDetail: "{{ dell_ome_device_idrac_ip }}"
                  AddressType: 30
                  Disabled: false
                  Exclude: false
              ConnectionProfileId: 0
              ConnectionProfile: >-
                {"profileName":"","profileDescription":"","type":"DISCOVERY","credentials":[{"id":0,"type":"WSMAN","authType":"Basic","modified":false,"credentials":{"username":"root","password":{{ dell_idrac_root_password }},"caCheck":false,"cnCheck":false,"port":443,"retries":3,"timeout":60,"isHttp":false,"keepAlive":false}}]}
              DeviceType:
                - 1000
          Schedule:
            RunNow: true
            RunLater: false
            Cron: startnow
            StartTime: ''
            EndTime: ''
          CreateGroup: true
          TrapDestination: false
        use_proxy: 'no'
        status_code: 201
        return_content: 'yes'
        validate_certs: 'no'
        force_basic_auth: 'yes'
        headers:
          Content-Type: application/json
          Accept: application/json
      register: ome_template_device_add_response

This is the error output I am receiving:

[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match ‘all’

PLAY [Add server into OME] ************************************************************************************************************************************************************************************************************

TASK [set_fact] ***********************************************************************************************************************************************************************************************************************
ok: [localhost] => {“ansible_facts”: {“random”: “437289450216118”}, “changed”: false}

TASK [Add device into OME for fresh discovery] ****************************************************************************************************************************************************************************************
[WARNING]: Module did not set no_log for password
fatal: [localhost]: FAILED! => {“access_control_allow_origin”: “*”, “changed”: false, “connection”: “close”, “content”: “{"error":{"code":"Base.1.0.GeneralError","message":"A general error has occurred. See ExtendedInfo for more information.","@Message.ExtendedInfo":[{"MessageId":"CGEN1008","RelatedProperties":,"Message":"Unable to process the request because an error occurred.","MessageArgs":,"Severity":"Critical","Resolution":"Retry the operation. If the issue persists, contact your system administrator."}]}}”, “content_length”: “399”, “content_security_policy”: “default-src ‘self’ ‘unsafe-eval’; connect-src *; style-src ‘self’ ‘unsafe-inline’ blob:; script-src ‘self’ ‘unsafe-inline’ ‘unsafe-eval’; img-src ‘self’ blob: data:”, “content_type”: “application/json;charset=UTF-8”, “date”: “Tue, 30 Apr 2024 10:23:17 GMT”, “elapsed”: 21, “json”: {“error”: {“@Message.ExtendedInfo”: [{“Message”: “Unable to process the request because an error occurred.”, “MessageArgs”: , “MessageId”: “CGEN1008”, “RelatedProperties”: , “Resolution”: “Retry the operation. If the issue persists, contact your system administrator.”, “Severity”: “Critical”}], “code”: “Base.1.0.GeneralError”, “message”: “A general error has occurred. See ExtendedInfo for more information.”}}, “msg”: “Status code was 400 and not [201]: HTTP Error 400: 400”, “odata_version”: “4.0”, “redirected”: false, “server”: “Apache”, “set_cookie”: “rememberMe=deleteMe; Path=/api; Max-Age=0; Expires=Mon, 29-Apr-2024 10:23:38 GMT; SameSite=lax”, “status”: 400, “strict_transport_security”: “max-age=31536000; includeSubDomains”, “url”: “Sherwin-Williams Paints, Stains, Supplies and Coating Solutions”, “x_content_type_options”: “nosniff”, “x_frame_options”: “SAMEORIGIN”, “x_xss_protection”: “1; mode=block”}

Any help appreciated, thanks in advance :).

Hi, I don’t have any OME in my lab so I can’t make any test, but perhaps I can provide some considerations.

ConnectionProfile: >-
  ..."credentials":{"username":"root","password":{{ dell_idrac_root_password }},...

Simply you can wrap your variables with ":

ConnectionProfile: >-
  ..."credentials":{"username":"root","password":"{{ dell_idrac_root_password }}",...

This will generate complete JSON strings, but this means the value will be serialized as dict, that causes the request body that received on the OEM side will look like this:

...
"ConnectionProfile": {
    "profileName": "",
    "profileDescription": "",
    ...
    "credentials": [
        {
            "id": 0,
            ...
            "credentials": {
                "username": "root",
                "password": "fakePassword",
                ...

However according to the OME API reference, the value of ConnectionProfile must be an escaped JSON string, instead of a dictionary:

...
"ConnectionProfile":"{\"profileName\":\"\",\"profileDescription\":\"\",\"type\":\"DISCOVERY\",\"credentials\":[{\"id\":0,\"type\":\"WSMAN\",\"authType\":\"Basic\",\"modified\":false,\"credentials\":{\"username\":\"root\",\"fakePassword\":\"calvin\",\"caCheck\":false,\"cnCheck\":false,\"port\":443,\"retries\":3,\"timeout\":60,\"isHttp\":false,\"keepAlive\":false}}]}",

So my recommendation is:

  • Store ConnectionProfile as an independent variable
    • _connection_profile_dict in following example
  • Then pass it by converting JSON, and store it as string instead of dict
    • {{ _connection_profile_dict | ansible.builtin.to_json | string }}

- name: Add device into OME for fresh discovery
  uri:
    url: https://httpdump.app/dumps/7042f3c3-f13d-42e5-9cb9-dbda93308302
    user: '{{ dell_ome_user }}@email.com'
    password: '{{ dell_ome_password }}'
    method: POST
    body_format: json
    body:
      DiscoveryConfigGroupName: Discovery-{{ random }}
      DiscoveryConfigGroupDescription: 'null'
      DiscoveryStatusEmailRecipient: email@email.com
      DiscoveryConfigModels:
        - DiscoveryConfigId: 331105536
          DiscoveryConfigDescription: ''
          DiscoveryConfigStatus: ''
          DiscoveryConfigTargets:
            - DiscoveryConfigTargetId: 0
              NetworkAddressDetail: "{{ dell_ome_device_idrac_ip }}"
              AddressType: 30
              Disabled: false
              Exclude: false
          ConnectionProfileId: 0
          ConnectionProfile: "{{ _connection_profile_dict | ansible.builtin.to_json | string }}"
          DeviceType:
            - 1000
      Schedule:
        RunNow: true
        RunLater: false
        Cron: startnow
        StartTime: ''
        EndTime: ''
      CreateGroup: true
      TrapDestination: false
    use_proxy: 'no'
    status_code: 201
    return_content: 'yes'
    validate_certs: 'no'
    force_basic_auth: 'yes'
    headers:
      Content-Type: application/json
      Accept: application/json
  register: ome_template_device_add_response
  vars:
    _connection_profile_dict:
      profileName: ""
      profileDescription: ""
      type: "DISCOVERY"
      credentials:
        - id: 0
          type: "WSMAN"
          authType: "Basic"
          modified: false
          credentials:
            username: "root"
            password: "{{ dell_idrac_root_password }}"
            caCheck: false
            cnCheck: false
            port: 443
            retries: 3
            timeout: 60
            isHttp: false
            keepAlive: false

This is probably easier because you won’t have to suffer through the escapes.

Hope this helps.
Again, I didn’t make any tests with this against actual OME, so sorry if it not works :disappointed:

3 Likes

That worked, thanks for a lot for your help! Ive learnt something new as well, much appreciated.

2 Likes