- Remove all ZITADEL_FIRSTINSTANCE_* environment variables - Fixes migration error: duplicate key constraint violation - Root cause: Bug in Zitadel v2.63.7 FirstInstance migration - Workaround: Complete initial setup via web UI - Upstream issue: https://github.com/zitadel/zitadel/issues/8791 Changes: - Clean up obsolete documentation (OIDC_AUTOMATION.md, SETUP_GUIDE.md, COLLABORA_SETUP.md) - Add PROJECT_REFERENCE.md for essential configuration info - Add force recreate functionality with clean database volumes - Update bootstrap instructions for web UI setup - Document one-time manual setup requirement for OIDC automation Zitadel now deploys successfully and is accessible at: https://zitadel.test.vrije.cloud 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
129 lines
4.1 KiB
Python
129 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Create a machine user in Zitadel using admin credentials.
|
|
This script creates a service account with a JWT key for API automation.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import requests
|
|
from urllib.parse import urlencode
|
|
|
|
def get_admin_token(domain, username, password):
|
|
"""Get access token using admin username/password."""
|
|
token_url = f"https://{domain}/oauth/v2/token"
|
|
|
|
data = {
|
|
"grant_type": "password",
|
|
"username": username,
|
|
"password": password,
|
|
"scope": "openid profile email urn:zitadel:iam:org:project:id:zitadel:aud",
|
|
}
|
|
|
|
response = requests.post(token_url, data=data)
|
|
if response.status_code == 200:
|
|
return response.json().get("access_token")
|
|
else:
|
|
raise Exception(f"Failed to get admin token: {response.status_code} - {response.text}")
|
|
|
|
def create_machine_user(domain, access_token, username, name):
|
|
"""Create a machine user."""
|
|
url = f"https://{domain}/management/v1/users/machine"
|
|
headers = {
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload = {
|
|
"userName": username,
|
|
"name": name,
|
|
"description": "Service account for automated API operations",
|
|
"accessTokenType": "ACCESS_TOKEN_TYPE_JWT",
|
|
}
|
|
|
|
response = requests.post(url, headers=headers, json=payload)
|
|
|
|
if response.status_code in [200, 201]:
|
|
return response.json().get("userId")
|
|
elif response.status_code == 409:
|
|
# User already exists, get the user ID
|
|
return find_machine_user(domain, access_token, username)
|
|
else:
|
|
raise Exception(f"Failed to create machine user: {response.status_code} - {response.text}")
|
|
|
|
def find_machine_user(domain, access_token, username):
|
|
"""Find existing machine user by username."""
|
|
url = f"https://{domain}/management/v1/users/_search"
|
|
headers = {
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload = {
|
|
"queries": [
|
|
{
|
|
"userNameQuery": {
|
|
"userName": username,
|
|
"method": "TEXT_QUERY_METHOD_EQUALS"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
response = requests.post(url, headers=headers, json=payload)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json().get("result", [])
|
|
if result:
|
|
return result[0].get("id")
|
|
return None
|
|
|
|
def create_machine_key(domain, access_token, user_id):
|
|
"""Create a JWT key for the machine user."""
|
|
url = f"https://{domain}/management/v1/users/{user_id}/keys"
|
|
headers = {
|
|
"Authorization": f"Bearer {access_token}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload = {
|
|
"type": "KEY_TYPE_JSON",
|
|
"expirationDate": "2030-01-01T00:00:00Z",
|
|
}
|
|
|
|
response = requests.post(url, headers=headers, json=payload)
|
|
|
|
if response.status_code in [200, 201]:
|
|
return response.json()
|
|
else:
|
|
raise Exception(f"Failed to create machine key: {response.status_code} - {response.text}")
|
|
|
|
def main():
|
|
if len(sys.argv) != 4:
|
|
print("Usage: create_machine_user.py <domain> <admin_username> <admin_password>")
|
|
sys.exit(1)
|
|
|
|
domain = sys.argv[1]
|
|
admin_username = sys.argv[2]
|
|
admin_password = sys.argv[3]
|
|
|
|
try:
|
|
# Get admin access token
|
|
print(f"Authenticating as admin...", file=sys.stderr)
|
|
access_token = get_admin_token(domain, admin_username, admin_password)
|
|
|
|
# Create machine user
|
|
print(f"Creating machine user 'api-automation'...", file=sys.stderr)
|
|
user_id = create_machine_user(domain, access_token, "api-automation", "API Automation Service")
|
|
print(f"Machine user ID: {user_id}", file=sys.stderr)
|
|
|
|
# Create JWT key
|
|
print(f"Creating JWT key...", file=sys.stderr)
|
|
key_data = create_machine_key(domain, access_token, user_id)
|
|
|
|
# Output the key as JSON
|
|
print(json.dumps(key_data, indent=2))
|
|
|
|
except Exception as e:
|
|
print(f"Error: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|