Skip to content

Commit 9395740

Browse files
Merge pull request #32 from nutanixdev/api_key
Add API key authentication code sample
2 parents a3ecd8f + 87f4368 commit 9395740

File tree

3 files changed

+229
-2
lines changed

3 files changed

+229
-2
lines changed

python/nutanix_objects/requirements.txt

100755100644
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
boto3==1.17.84
2-
black==20.8b1
2+
black==24.3.0
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
"""
2+
Use the Nutanix v4 API SDKs to setup API key authentication
3+
Requires Prism Central 2024.3 or later and AOS 7.0 or later
4+
"""
5+
6+
import getpass
7+
import argparse
8+
import sys
9+
import urllib3
10+
import json
11+
12+
import ntnx_vmm_py_client
13+
from ntnx_vmm_py_client import Configuration as VMMConfiguration
14+
from ntnx_vmm_py_client import ApiClient as VMMClient
15+
16+
import ntnx_iam_py_client
17+
from ntnx_iam_py_client import Configuration as IAMConfiguration
18+
from ntnx_iam_py_client import ApiClient as IAMClient
19+
from ntnx_iam_py_client.rest import ApiException as IAMException
20+
21+
from ntnx_iam_py_client import UsersApi, AuthorizationPoliciesApi
22+
from ntnx_iam_py_client import User, UserType, CreationType, UserStatusType
23+
from ntnx_iam_py_client import Key, KeyKind
24+
from ntnx_iam_py_client import AuthorizationPolicy, AuthorizationPolicyType
25+
from ntnx_iam_py_client import EntityFilter, IdentityFilter
26+
27+
from tme.utils import Utils
28+
29+
30+
def main():
31+
"""
32+
suppress warnings about insecure connections
33+
please consider the security implications before
34+
doing this in a production environment
35+
"""
36+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
37+
38+
"""
39+
setup the command line parameters
40+
for this example only two parameters are required
41+
- the Prism Central IP address or FQDN
42+
- the Prism Central username; the script will prompt for the user's password
43+
so that it never needs to be stored in plain text
44+
"""
45+
parser = argparse.ArgumentParser()
46+
parser.add_argument("pc_ip", help="Prism Central IP address or FQDN")
47+
parser.add_argument("username", help="Prism Central username")
48+
parser.add_argument(
49+
"-p", "--poll", help="Time between task polling, in seconds", default=1
50+
)
51+
args = parser.parse_args()
52+
53+
# get the cluster password
54+
cluster_password = getpass.getpass(
55+
prompt="Enter your Prism Central \
56+
password: ",
57+
stream=None,
58+
)
59+
60+
pc_ip = args.pc_ip
61+
username = args.username
62+
63+
# make sure the user enters a password
64+
if not cluster_password:
65+
while not cluster_password:
66+
print(
67+
"Password cannot be empty. \
68+
Enter a password or Ctrl-C/Ctrl-D to exit."
69+
)
70+
cluster_password = getpass.getpass(
71+
prompt="Enter your Prism Central password: ", stream=None
72+
)
73+
74+
try:
75+
# create utils instance for re-use later
76+
utils = Utils(pc_ip=pc_ip, username=username, password=cluster_password)
77+
78+
vmm_config = VMMConfiguration()
79+
iam_config = IAMConfiguration()
80+
for config in [iam_config]:
81+
# create the configuration instances
82+
config.host = pc_ip
83+
config.username = username
84+
config.password = cluster_password
85+
config.verify_ssl = False
86+
config.debug = False
87+
88+
# setup VMM configuration
89+
# note we are NOT setting the username and password at this time
90+
# later we will list VMs using API key authentication
91+
vmm_config.host = pc_ip
92+
vmm_config.port = "9440"
93+
vmm_config.verify_ssl = False
94+
vmm_config.debug = False
95+
96+
vmm_config.logger_file = "./vmm.log"
97+
iam_config.logger_file = "./iam.log"
98+
99+
# before configuring API key auth, we need to get
100+
# the extId of an existing role
101+
# for this demo, we will use the built-in 'Super Admin' role
102+
iam_client = IAMClient(configuration=iam_config)
103+
iam_instance = ntnx_iam_py_client.api.RolesApi(api_client=iam_client)
104+
print("Retrieving filtered role list ...")
105+
role_list = iam_instance.list_roles(
106+
async_req=False, _filter="contains(displayName, 'Super')"
107+
)
108+
if len(role_list.data) > 0:
109+
super_admin_ext_id = role_list.data[0].ext_id
110+
print(f"Super Admin role extId: {super_admin_ext_id}")
111+
else:
112+
print("No role found containing the word \"Super\". Exiting ...")
113+
sys.exit()
114+
115+
vmm_client = VMMClient(configuration=vmm_config)
116+
vmm_instance = ntnx_vmm_py_client.api.VmApi(api_client=vmm_client)
117+
118+
sa_username = "api_key_service_account"
119+
sa_email = "<email_address_here>"
120+
sa_display_name = "API key service account"
121+
sa_description = "Service account for API key authentication"
122+
key_name = "service_account_api_key"
123+
acp_display_name = "API Key Auth Policy"
124+
125+
print("\nThe following configuration will be used for API key authentication.")
126+
print(f" Username: {sa_username}")
127+
print(f" Description: {sa_description}")
128+
print(f" Email: {sa_email}")
129+
print(f" Display name: {sa_display_name}")
130+
print(f" Key name: {key_name}")
131+
print(f" Authorization policy display name: {acp_display_name}\n")
132+
133+
confirm_continue = utils.confirm("Continue API key configuration?")
134+
135+
if confirm_continue:
136+
# create service account
137+
service_account = User(
138+
username=sa_username,
139+
email=sa_email,
140+
display_name=sa_display_name,
141+
description=sa_description,
142+
creation_type=CreationType.USERDEFINED,
143+
status=UserStatusType.ACTIVE,
144+
user_type=UserType.SERVICE_ACCOUNT,
145+
)
146+
147+
iam_instance = UsersApi(api_client=iam_client)
148+
create_sa = iam_instance.create_user(async_req=False, body=service_account)
149+
150+
if create_sa:
151+
print("Service account created successfully.")
152+
else:
153+
print("Service account creation failed. Check iam.log for details.")
154+
sys.exit()
155+
156+
# get the new service account user's ext_id
157+
sa_ext_id = create_sa.data.ext_id
158+
159+
# create API key
160+
api_key = Key(name=key_name, key_type=KeyKind.API_KEY)
161+
162+
create_key = iam_instance.create_user_key(
163+
async_req=False, userExtId=sa_ext_id, body=api_key
164+
)
165+
if create_key:
166+
print("API key created successfully.")
167+
print(
168+
f"The API key will only be shown ONCE: {create_key.data.key_details.api_key}"
169+
)
170+
api_key_value = create_key.data.key_details.api_key
171+
172+
entities = [EntityFilter({"*": {"*": {"eq": "*"}}})]
173+
174+
identities = [IdentityFilter({"user": {"uuid": {"anyof": [sa_ext_id]}}})]
175+
176+
# create authorization policy for new service account
177+
iam_instance = AuthorizationPoliciesApi(api_client=iam_client)
178+
auth_policy = AuthorizationPolicy(
179+
display_name=acp_display_name,
180+
description="Authorization policy for use with API key service accounts",
181+
authorization_policy_type=AuthorizationPolicyType.USER_DEFINED,
182+
entities=entities,
183+
identities=identities,
184+
role=super_admin_ext_id,
185+
)
186+
187+
create_acp = iam_instance.create_authorization_policy(
188+
async_req=False, body=auth_policy
189+
)
190+
if create_acp:
191+
print("Authorization policy created successfully.")
192+
else:
193+
print(
194+
"Authorization policy creation failed. Check iam.log for details."
195+
)
196+
197+
auth_with_key = utils.confirm(
198+
"\nAll configuration completed successfully. Attempt to list VMs with API authentication?"
199+
)
200+
if auth_with_key:
201+
print(
202+
"Attempting to list Prism Central VMs using API key authentication ..."
203+
)
204+
vmm_client.add_default_header(
205+
header_name="X-Ntnx-Api-Key", header_value=api_key_value
206+
)
207+
vm_list = vmm_instance.list_vms(async_req=False)
208+
if vm_list:
209+
print(
210+
f"{len(vm_list.data)} VMs found. API key authentication successful."
211+
)
212+
else:
213+
print("VM list operation failed. Check vmm.log for details.")
214+
else:
215+
print("API key authentication cancelled.")
216+
else:
217+
print("API key configuration cancelled.")
218+
sys.exit()
219+
220+
except IAMException as iam_exception:
221+
print(
222+
f"Error sending request. Exception details:\n {json.loads(iam_exception.body)['data']['error'][0]['message']}"
223+
)
224+
225+
226+
if __name__ == "__main__":
227+
main()

python/v4api_sdk/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
requests==2.31.0
1+
requests==2.32.2
22
ruff==0.1.15
33
ntnx_vmm_py_client==4.0.1
44
ntnx_lifecycle_py_client==4.0.1

0 commit comments

Comments
 (0)