Skip to content

Commit 4642162

Browse files
authored
[minor change] Add nd_api_key module
1 parent 08b9360 commit 4642162

File tree

2 files changed

+465
-0
lines changed

2 files changed

+465
-0
lines changed

plugins/modules/nd_api_key.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
# Copyright: (c) 2025, Dev Sinha (@DevSinha13) <[email protected]>
5+
6+
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
7+
8+
from __future__ import absolute_import, division, print_function
9+
10+
__metaclass__ = type
11+
12+
ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"}
13+
14+
DOCUMENTATION = r"""
15+
---
16+
module: nd_api_key
17+
version_added: "1.4.0"
18+
short_description: Manage API keys in Nexus Dashboard
19+
description:
20+
- Manage API keys in Cisco Nexus Dashboard (ND).
21+
- It supports creating, updating, querying, and deleting API keys.
22+
author:
23+
- Dev Sinha (@DevSinha13)
24+
options:
25+
api_key_id:
26+
description:
27+
- The ID of the API key.
28+
type: str
29+
api_key_name:
30+
description:
31+
- The name of the API key.
32+
- The API key name must be between 1 and 32 characters in length.
33+
- Only alphanumeric characters, underscores, periods, and hyphens are allowed.
34+
type: str
35+
aliases: [ description ]
36+
annotations:
37+
description:
38+
- The annotations attached to the API key.
39+
- Annotations are optional key-value pairs used to add custom metadata to the API key.
40+
type: dict
41+
state:
42+
description:
43+
- The desired state of the API key.
44+
type: str
45+
choices: [ present, absent, query ]
46+
default: present
47+
extends_documentation_fragment:
48+
- cisco.nd.modules
49+
- cisco.nd.check_mode
50+
"""
51+
52+
EXAMPLES = r"""
53+
- name: Create a new API key
54+
cisco.nd.nd_api_key:
55+
api_key_name: "ansible_test_key"
56+
annotations:
57+
owner: "ansible"
58+
purpose: "automation"
59+
state: present
60+
register: result
61+
62+
- name: Create API key with minimal config
63+
cisco.nd.nd_api_key:
64+
api_key_name: "simple_key"
65+
state: present
66+
67+
- name: Update API key annotations
68+
cisco.nd.nd_api_key:
69+
api_key_name: "existing_key"
70+
annotations:
71+
owner: "updated_owner"
72+
environment: "production"
73+
state: present
74+
75+
- name: Query an existing API key by name
76+
cisco.nd.nd_api_key:
77+
api_key_name: "ansible_test_key"
78+
state: query
79+
register: query_result
80+
81+
- name: Query an existing API key by ID
82+
cisco.nd.nd_api_key:
83+
api_key_id: "{{ result.current.id }}"
84+
state: query
85+
register: query_result_by_id
86+
87+
- name: Query all API keys
88+
cisco.nd.nd_api_key:
89+
state: query
90+
register: all_keys
91+
92+
- name: Delete an API key by ID
93+
cisco.nd.nd_api_key:
94+
api_key_id: "{{ result.current.id }}"
95+
state: absent
96+
97+
- name: Delete an API key by name
98+
cisco.nd.nd_api_key:
99+
api_key_name: "ansible_test_key"
100+
state: absent
101+
"""
102+
103+
RETURN = r"""
104+
"""
105+
106+
from copy import deepcopy
107+
import re
108+
from ansible.module_utils.basic import AnsibleModule
109+
from ansible_collections.cisco.nd.plugins.module_utils.nd import NDModule, nd_argument_spec
110+
111+
112+
def main():
113+
argument_spec = nd_argument_spec()
114+
argument_spec.update(
115+
api_key_id=dict(type="str"),
116+
api_key_name=dict(type="str", aliases=["description"]),
117+
annotations=dict(type="dict"),
118+
state=dict(type="str", default="present", choices=["present", "absent", "query"]),
119+
)
120+
121+
module = AnsibleModule(
122+
argument_spec=argument_spec,
123+
supports_check_mode=True,
124+
required_if=[
125+
["state", "present", ["api_key_name"]],
126+
["state", "absent", ["api_key_name", "api_key_id"], True],
127+
],
128+
)
129+
130+
nd = NDModule(module)
131+
132+
api_key_id = nd.params.get("api_key_id")
133+
api_key_name = nd.params.get("api_key_name")
134+
annotations = nd.params.get("annotations")
135+
state = nd.params.get("state")
136+
137+
if annotations is None:
138+
annotations = {}
139+
140+
path = "/api/v1/infra/aaa/apiKeys"
141+
if api_key_id:
142+
nd.existing = nd.previous = deepcopy(nd.query_obj("{0}/{1}".format(path, api_key_id)))
143+
elif api_key_name:
144+
nd.existing = nd.previous = deepcopy(nd.get_obj(path, key="apiKeys", apiKeyName=api_key_name))
145+
else:
146+
nd.existing = nd.previous = nd.query_objs(path, key="apiKeys")
147+
148+
if state == "present":
149+
150+
if len(api_key_name) > 32 or len(api_key_name) < 1:
151+
nd.fail_json("A length of 1 to 32 characters is allowed.")
152+
elif re.search(r"[^a-zA-Z0-9_.-]", api_key_name):
153+
nd.fail_json("API Key name contains invalid characters. Valid characters include letters, digits, '_', '.', and '-'.")
154+
155+
payload = {
156+
"apiKeyName": api_key_name,
157+
"annotations": annotations,
158+
}
159+
160+
nd.sanitize(payload)
161+
162+
if not module.check_mode:
163+
if nd.existing:
164+
if nd.existing.get("apiKeyName") != api_key_name:
165+
update_path = "{0}/{1}".format(path, api_key_id)
166+
nd.request(update_path, method="PUT", data=payload)
167+
nd.existing = nd.query_obj(update_path)
168+
else:
169+
nd.existing = nd.request(path, method="POST", data=payload)
170+
else:
171+
nd.existing = nd.proposed
172+
173+
elif state == "absent":
174+
if nd.existing:
175+
if not module.check_mode:
176+
nd.request("{0}/{1}".format(path, nd.existing["id"]), method="DELETE")
177+
178+
nd.existing = {}
179+
180+
nd.exit_json()
181+
182+
183+
if __name__ == "__main__":
184+
main()

0 commit comments

Comments
 (0)