#!/usr/bin/env python3 """ Automate creation of Zitadel API service user and Personal Access Token. This script logs in as admin and creates a machine user with a PAT for API automation. """ import requests import sys import time import re from urllib.parse import urlparse, parse_qs def login_and_get_session(domain, username, password): """Login to Zitadel and get authenticated session cookies.""" session = requests.Session() session.headers.update({ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' }) # Start login flow login_url = f"https://{domain}/ui/login/loginname" print(f"šŸ“” Initiating login to {domain}...") # Get login page to establish session resp = session.get(login_url, allow_redirects=True) # Submit username login_data = { 'loginName': username } resp = session.post(login_url, data=login_data, allow_redirects=True) # Submit password password_url = f"https://{domain}/ui/login/password" password_data = { 'password': password } resp = session.post(password_url, data=password_data, allow_redirects=True) if 'set-cookie' in resp.headers or len(session.cookies) > 0: print("āœ… Login successful!") return session else: print(f"āŒ Login failed. Status: {resp.status_code}") print(f"Response: {resp.text[:500]}") return None def create_machine_user(session, domain): """Create a machine user via Management API.""" api_url = f"https://{domain}/management/v1/users/machine" print("šŸ¤– Creating API automation service user...") payload = { "userName": "api-automation", "name": "API Automation Service", "description": "Service account for automated OIDC app provisioning", "accessTokenType": "ACCESS_TOKEN_TYPE_BEARER" } resp = session.post(api_url, json=payload) if resp.status_code in [200, 201]: data = resp.json() user_id = data.get('userId') print(f"āœ… Machine user created: {user_id}") return user_id elif resp.status_code == 409: print("ā„¹ļø Machine user already exists") # Try to get existing user list_url = f"https://{domain}/management/v1/users/_search" search_payload = { "query": { "userName": "api-automation" } } resp = session.post(list_url, json=search_payload) if resp.status_code == 200: users = resp.json().get('result', []) if users: user_id = users[0].get('id') print(f"āœ… Found existing user: {user_id}") return user_id return None else: print(f"āŒ Failed to create machine user. Status: {resp.status_code}") print(f"Response: {resp.text}") return None def create_pat(session, domain, user_id): """Create a Personal Access Token for the machine user.""" pat_url = f"https://{domain}/management/v1/users/{user_id}/pats" print("šŸ”‘ Creating Personal Access Token...") payload = { "expirationDate": "2099-12-31T23:59:59Z" } resp = session.post(pat_url, json=payload) if resp.status_code in [200, 201]: data = resp.json() token = data.get('token') if token: print("āœ… Personal Access Token created successfully!") return token else: print("āš ļø PAT created but token not in response") print(f"Response: {resp.text}") return None else: print(f"āŒ Failed to create PAT. Status: {resp.status_code}") print(f"Response: {resp.text}") return None def main(): if len(sys.argv) != 4: print("Usage: python3 bootstrap_api_token.py ") print("Example: python3 bootstrap_api_token.py zitadel.test.vrije.cloud 'admin@test.zitadel.test.vrije.cloud' 'password123'") sys.exit(1) domain = sys.argv[1] username = sys.argv[2] password = sys.argv[3] print(f""" šŸš€ Zitadel API Token Bootstrap ================================ Domain: {domain} Admin: {username} """) # Step 1: Login session = login_and_get_session(domain, username, password) if not session: print("\nāŒ Failed to establish session") sys.exit(1) # Small delay to ensure session is established time.sleep(2) # Step 2: Create machine user user_id = create_machine_user(session, domain) if not user_id: print("\nāŒ Failed to create or find machine user") sys.exit(1) # Small delay time.sleep(1) # Step 3: Create PAT token = create_pat(session, domain, user_id) if not token: print("\nāŒ Failed to create Personal Access Token") sys.exit(1) print(f""" āœ… SUCCESS! API automation is ready. šŸ“‹ Personal Access Token: {token} šŸ” Add this to your secrets file: zitadel_api_token: {token} Then re-run: ansible-playbook -i hcloud.yml playbooks/deploy.yml """) if __name__ == '__main__': main()