diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/.env.example b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/.env.example new file mode 100644 index 000000000..cac0505bf --- /dev/null +++ b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/.env.example @@ -0,0 +1,4 @@ +AUTH0_DOMAIN=dev-xxxxxxxxxx.us.auth0.com +AUTH0_AUDIENCE=https://bedrock-agentcore.us-west-2.amazonaws.com +AWS_REGION=us-west-2 +AWS_ACCOUNT_ID=123456789 diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/DCR_registry_search_mcp_in_kiro.ipynb b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/DCR_registry_search_mcp_in_kiro.ipynb new file mode 100644 index 000000000..24af31334 --- /dev/null +++ b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/DCR_registry_search_mcp_in_kiro.ipynb @@ -0,0 +1,360 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Calling Registry as an MCP Tool from Kiro\n", + "\n", + "#### Discover agentic capabilities from the AWS Agent Registry across your organization directly from your Kiro IDE, without writing a single line of code\n", + "\n", + "## Overview\n", + "Being able to search across a registry of agentic capabilities built across your organization directly in your IDE like Kiro is a superpower. This example shows how to configure an AWS Agent Registry with Auth0 Dynamic Client Registration (DCR) to enable secure, zero-config discovery of agents and tools.\n", + "\n", + "**What is Dynamic Client Registration?** \n", + "\n", + "Dynamic Client Registration is an OAuth and OpenID Connect protocol that lets client applications automatically register with an authorization server instead of requiring manual pre-registration. The DCR protocol is formally defined in RFC 7591, with optional registration management extensions in RFC 7592. It was designed to work within the Open Authorization (OAuth) and OpenID Connect (OIDC) ecosystems. It enables automatic creation of client IDs, secrets, and metadata (redirect URIs, scopes), often used for automation, AI agents, and dynamic scaling.\n", + "\n", + "In the case of using your AWS Registry MCP in your IDE, Dynamic Client Registration allows your IDE to automatically get its own unique credentials for the AWS Registry access instead of requiring a manual setup. This enables the IDE to programmatically request and refresh its own access tokens, completely removing the need for you to manually copy and paste them.\n", + "\n", + "![Kiro MCP JSON](images/0_authflow_dcr.png)\n", + "\n", + "### Key Features\n", + "- Create an Auth0-secured AWS Agent Registry with CUSTOM_JWT authorizer\n", + "- Seed the registry with sample agent records\n", + "- Search records using OAuth bearer tokens\n", + "- Connect the registry as an MCP server in Kiro for IDE-native discovery" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tutorial Details\n", + "\n", + "| Information | Details |\n", + "|:---|:---|\n", + "| Tutorial type | Interactive |\n", + "| AgentCore components | AWS Agent Registry |\n", + "| Auth method | Auth0 DCR (Dynamic Client Registration) |\n", + "| Inbound Auth IdP | Auth0 (CUSTOM_JWT) |\n", + "| Outbound Auth | OAuth Bearer Token |\n", + "| Tutorial components | Create Auth0 DCR Server, OAuth search, MCP setup on Kiro |\n", + "| Tutorial vertical | Cross-vertical |\n", + "| Example complexity | Easy |\n", + "| SDK used | boto3 |\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Prerequisites\n", + "\n", + "- **Auth0 account** with a tenant configured\n", + "- **Python 3.10+** with `boto3`, `python-dotenv`, `requests`\n", + "- **Kiro/Claude IDE** (for the MCP integration step)\n", + "- `.env` file configured from `.env.example`\n", + "\n", + "\n", + "\n", + "_______________________________________" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 1: Auth0 Setup" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "### 1. Create an Auth0 Account & Tenant\n", + "\n", + "1. Sign up at [auth0.com](https://auth0.com) if you don't already have an account\n", + "2. Create a new tenant (or use an existing one) — this acts as your authorization server\n", + "3. Note your **Auth0 Domain** (e.g., `your-tenant.auth0.com`)\n", + "4. Go to Dashboard > Settings > Advanced and enable Dynamic Client Registration (DCR).\n", + "\n", + "### 2. Register an API (Resource Server)\n", + "\n", + "1. Navigate to **Applications → APIs** in the Auth0 Dashboard\n", + "2. Click **Create API**\n", + "3. Provide a **Name** (e.g., `AWS Agent Registry API`)\n", + "4. Set the **Identifier (Audience)** to URL: `https://bedrock-agentcore.us-west-2.amazonaws.com` \n", + "5. Set **Allow Skipping User Consent** to `true` (optional but convenient for testing)\n", + "6. Select the **Signing Algorithm** — `RS256` is recommended\n", + "7. As a best practice for security purpose\n", + " - Set **Token Lifetime** to 3600 seconds (1 hour) or less\n", + " - Go to **Settings → Advanced Settings → Grant Types** and enable **Authorization Code, Refresh Token, Client Credentials**\n", + "8. Add a user to the API's assigned users list. This user will be used to generate the access token for the registry search. Go to **User Management** > **Users** > **Add a user** .\n", + " \n", + "\n", + "> **⚠️ Caution:** DCR enables dynamic registration of clients — your Auth0 tenant will see third-party clients created automatically by each MCP client (e.g., Kiro) that connects. Using Dynamic Client Registration effectively requires attention to both its security implications and the practical realities of deploying it at scale. Because DCR automates registration, you must ensure that only trusted clients can register and that the registration workflow cannot be exploited.\n", + "\n", + "\n", + "### 3. Enable authentication at domain level\n", + "\n", + "Since DCR creates a brand new client app in Auth0, it won't have any connections enabled by default. Auth0 needs to know which connections (login methods) the client can use\n", + "1. Navigate to **Authentication → Database → Username-Password-Authentication-> Settings -> toogle on [Promote Connection to Domain Level]** in the Auth0 Dashboard\n", + "\n", + "\n", + "### 4. Configure .env\n", + "\n", + "Copy `.env.example` to `.env` and fill in your values.\n", + "\n", + "### 5. Create the Registry and Seed it with sample records.\n", + "Follow Step 2 and 3 in the notebook, to create the registry and seed it with sample records.\n", + "\n", + "\n", + "### 6. Add your AWS Agent Registry API as Allowed Audience to the Oauth on regitsry.\n", + "Kiro sends the MCP server URL as the `audience` in the Auth0 authorization request (Otherwise it wont find the service). This will be a two step process :\n", + "1. Create an API in Auth0 with the MCP URL as the identifier.\n", + "2. Update the Registry's OAuth configuration to allow the API as an allowed audience.\n", + "\n", + "For this lab: \n", + "1. Note the Registry ID created. In the Auth0 dashboard, navigate to **Applications → APIs** and create an API using the registry's MCP endpoint URL as **Identifier**.\n", + "Mcp url: `https://bedrock-agentcore.us-west-2.amazonaws.com/registry//mcp`\n", + "\n", + "2. In this lab, you do not need to manually update the Auth configurations of the Registry Auth configuration. The `create_registry` helper for this notebook has been written to automatically add `https://bedrock-agentcore.us-west-2.amazonaws.com/registry//mcp` to the registry's `allowedAudience` after registry creation. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Step 2: Create Registry with Auth0 CUSTOM_JWT Authorizer\n", + "\n", + "This creates a new AWS Agent Registry configured with Auth0 as the OAuth identity provider. The helper:\n", + "1. Creates the registry with a `CUSTOM_JWT` authorizer pointing to your Auth0 tenant's OIDC discovery URL\n", + "2. Polls until the registry reaches `READY` status\n", + "3. Automatically adds the MCP endpoint URL to the registry's `allowedAudience`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install python-dotenv strands-agents bedrock-agentcore bedrock-agentcore-starter-toolkit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.insert(0, os.path.abspath('..'))\n", + "from seed_records import create_registry, seed\n", + "\n", + "registry = create_registry(\n", + " name=\"auth0-demo-registry-dcr\",\n", + " description=\"Demo registry with Auth0 OAuth for notebook walkthrough\",\n", + ")\n", + "registry_id = registry[\"registryId\"]\n", + "print(f\"Registry ID: {registry_id}\")\n", + "print(f\"Status: {registry['status']}\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# STEP 3: Seed Registry with Sample Capability Records\n", + "\n", + "\n", + "We'll populate the registry with four sample agent records (weather, order-status, customer-support, inventory-lookup). Each record is created as `DRAFT`, submitted for approval, and auto-approved so it appears in search results.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from seed_records import seed\n", + "\n", + "records = seed(registry_id=registry_id)\n", + "print(f\"\\nSeeded {len(records)} records:\")\n", + "for r in records:\n", + " print(f\" • {r['name']} ({r['recordId']})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "from seed_records import _cp_client\n", + "cp = _cp_client()\n", + "for r in cp.list_registries()['registries']:\n", + " if r['name'] == 'auth0-demo-registry-dcr':\n", + " print(f'Registry: {r[\"registryId\"]}')\n", + " recs = cp.list_registry_records(registryId=r['registryId']).get('registryRecords', [])\n", + " for rec in recs:\n", + " print(f'{rec[\"recordId\"]:15s} {rec[\"status\"]:10s} {rec[\"name\"]}')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# STEP 4: Create another API in Auth0 with the MCP URL as the identifier. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Kiro sends the MCP server URL as the `audience` in the Auth0 authorization request (Otherwise it wont find the service). This will be a two step process :\n", + "1. Create an API in Auth0 with the MCP URL as the identifier.\n", + "2. Update the Registry's OAuth configuration to allow the API as an allowed audience.\n", + "\n", + "Use the registry_id that was created above to generate the MCP url `https://bedrock-agentcore.us-west-2.amazonaws.com/registry//mcp`\n", + "\n", + "- [DO THIS]In the Auth0 dashboard, navigate to **Applications → APIs** and create an API using the registry's MCP endpoint URL as **Identifier**.\n", + "- In this lab,the `create_registry` helper for this notebook automatically adds `https://bedrock-agentcore.us-west-2.amazonaws.com/registry//mcp` to the registry's `allowedAudience` after registry creation. No action needed here.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# STEP 5 : Add AWS Agent Registry MCP Server to Kiro" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "Now that the registry is working with OAuth search, add it to Kiro as an MCP server for IDE-native discovery.\n", + "\n", + "### 4.1 Add to `mcp.json`\n", + "\n", + "Add the following to your Kiro MCP configuration (`.kiro/settings/mcp.json`):\n", + "\n", + "```json\n", + "{ \"mcpServers\" : {\n", + "\"dcr-registry-server_xx\": {\n", + " \"type\": \"http\",\n", + " \"url\": \"https://bedrock-agentcore.us-west-2.amazonaws.com/registry//mcp/\",\n", + " \"disabled\": false\n", + " }\n", + "}\n", + "}\n", + "\n", + "```\n", + "![Kiro MCP JSON](images/1_kiro_mcp_json.png)\n", + "\n", + "### 4.2 Authenticate\n", + "\n", + "Enable and authenticate the MCP on your Kiro/Claude\n", + "When Kiro connects to the MCP server, it will:\n", + "- Discover the Auth0 authorization server via the registry's well-known endpoint\n", + "- Use DCR to auto-register as an OAuth client (POST /oidc/register)\n", + "- Obtain an access token via PKCE authorization code flow\n", + "- Use the token to call the registry search MCP\n", + "\n", + "| Authorization PKCE | Successful Auth |\n", + "|:---:|:---:|\n", + "| ![Authorization PKCE](images/2_authorization_pkce.png) | ![Successful Auth](images/3_successful_auth.png) |\n", + "\n", + "\n", + "### 4.3 Search from Kiro\n", + "Open Kiro chat and ask it to search the registry:\n", + "\n", + "\"Use the AWS registry to search for weather records\"\n", + "\n", + "![Kiro MCP JSON](images/4_kiro_search.png) \n", + "\n", + "### Useful Reference Commands\n", + "\n", + "View the authorization server metadata:\n", + "\n", + "```\n", + "curl -X POST https:///oidc/register\\\n", + " -H \"Content-Type: application/json\" \\\n", + " -d '{\n", + " \"client_name\": \"test-mcp-client\",\n", + " \"redirect_uris\": [\"http://localhost:65358/callback\"],\n", + " \"grant_types\": [\"authorization_code\"],\n", + " \"response_types\": [\"code\"],\n", + " \"token_endpoint_auth_method\": \"none\"\n", + " }'\n", + "\n", + "```\n", + "\n", + "Manually register a client via DCR:\n", + "\n", + "```\n", + "https:///.well-known/oauth-authorization-server\n", + "```\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clean Up" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#Cleanup your registry and records\n", + "\n", + "from seed_records import delete_registry\n", + "delete_registry(registry_id)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.14.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/0_authflow_dcr.png b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/0_authflow_dcr.png new file mode 100644 index 000000000..041037093 Binary files /dev/null and b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/0_authflow_dcr.png differ diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/1_kiro_mcp_json.png b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/1_kiro_mcp_json.png new file mode 100644 index 000000000..90fef26f8 Binary files /dev/null and b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/1_kiro_mcp_json.png differ diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/2_authorization_pkce.png b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/2_authorization_pkce.png new file mode 100644 index 000000000..3c4a5928b Binary files /dev/null and b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/2_authorization_pkce.png differ diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/3_successful_auth.png b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/3_successful_auth.png new file mode 100644 index 000000000..310c6b01c Binary files /dev/null and b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/3_successful_auth.png differ diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/4_kiro_search.png b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/4_kiro_search.png new file mode 100644 index 000000000..4b26bc229 Binary files /dev/null and b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/images/4_kiro_search.png differ diff --git a/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/seed_records.py b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/seed_records.py new file mode 100644 index 000000000..a0a072584 --- /dev/null +++ b/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/seed_records.py @@ -0,0 +1,288 @@ +#!/usr/bin/env python3 +"""Auth0 OAuth utilities for AWS AgentCore Registry: create, seed, and search. + +This script provides end-to-end tooling for setting up and populating an +AWS AgentCore Registry that uses Auth0 CUSTOM_JWT authorization. + +Key capabilities: + - Authenticate via Auth0 client-credentials OAuth flow + - Create a new registry with CUSTOM_JWT authorizer backed by Auth0 + - Seed the registry with sample agent records (weather, order-status, + customer-support, inventory-lookup) and auto-approve them + - Search registry records using OAuth bearer tokens + +Configuration is loaded from a .env file (see .env.example) and requires: + AWS_REGION, AWS_ACCOUNT_ID, + AUTH0_DOMAIN, AUTH0_AUDIENCE. + +Usage: + # As a module + from seed_records import create_registry, seed, search, get_token + + # As a script — seeds records into the registry specified by REGISTRY_ID + python seed_records.py +""" + +import boto3 +import json +import logging +import os +import time +from pathlib import Path +from dotenv import load_dotenv + +load_dotenv(Path(__file__).parent / ".env") +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") +logger = logging.getLogger(__name__) + +REGION = os.getenv("AWS_REGION", "us-west-2") +ACCOUNT_ID = os.getenv("AWS_ACCOUNT_ID") +AUTH0_DOMAIN = os.getenv("AUTH0_DOMAIN") +AUDIENCE = os.getenv("AUTH0_AUDIENCE") + + +def _registry_arn(registry_id): + return f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:registry/{registry_id}" + + +def _cp_client(): + return boto3.client("bedrock-agentcore-control", region_name=REGION) + + +def _dp_client(): + return boto3.client("bedrock-agentcore", region_name=REGION) + + +# ── Registry ────────────────────────────────────────────────────────────────── + + +def create_registry( + name="auth0-oauth-registry", + description="Registry with Auth0 OAuth authentication", + poll_interval=5, + max_wait=150, +): + """Create an AgentCore registry with Auth0 CUSTOM_JWT authorizer. + + Returns dict with registryId, registryArn, and status. + """ + cp = _cp_client() + discovery_url = f"https://{AUTH0_DOMAIN}/.well-known/openid-configuration" + + logger.info("Creating registry '%s' with CUSTOM_JWT authorizer...", name) + resp = cp.create_registry( + name=name, + description=description, + authorizerType="CUSTOM_JWT", + authorizerConfiguration={ + "customJWTAuthorizer": { + "discoveryUrl": discovery_url, + "allowedAudience": [AUDIENCE], + } + }, + ) + registry_arn = resp["registryArn"] + registry_id = registry_arn.split("/")[-1] + logger.info("Created registry %s (%s)", registry_id, registry_arn) + + status = "UNKNOWN" + elapsed = 0 + while elapsed < max_wait: + time.sleep(poll_interval) + elapsed += poll_interval + info = cp.get_registry(registryId=registry_id) + status = info.get("status", "UNKNOWN") + logger.info("[%ds] status=%s", elapsed, status) + if status == "READY": + break + else: + logger.warning("Registry not READY after %ds — continuing anyway", max_wait) + + update_registry_audience_with_mcp_url(registry_id) + logger.info("Added MCP URL to allowedAudience") + + result = {"registryId": registry_id, "registryArn": registry_arn, "status": status} + logger.info("Done: %s", result) + return result + + +def update_registry_audience_with_mcp_url(registry_id): + """Add the MCP endpoint URL to the registry's allowedAudience.""" + cp = _cp_client() + dp = _dp_client() + registry = cp.get_registry(registryId=registry_id) + jwt_config = registry["authorizerConfiguration"]["customJWTAuthorizer"] + mcp_url = f"{dp.meta.endpoint_url}/registry/{registry_id}/mcp" + audience = list(set(jwt_config.get("allowedAudience", []) + [mcp_url])) + cp.update_registry( + registryId=registry_id, + authorizerConfiguration={ + "optionalValue": { + "customJWTAuthorizer": { + "discoveryUrl": jwt_config["discoveryUrl"], + "allowedAudience": audience, + } + } + }, + ) + while True: + status = cp.get_registry(registryId=registry_id)["status"] + if status != "UPDATING": + break + time.sleep(2) + return cp.get_registry(registryId=registry_id) + + +# ── Seed ────────────────────────────────────────────────────────────────────── + +RECORDS = [ + { + "name": "weather_agent", + "description": "Retrieves current weather conditions and 5-day forecasts for any city worldwide. Provides temperature, humidity, wind speed, and precipitation data.", + "descriptorType": "CUSTOM", + "descriptors": { + "custom": { + "inlineContent": json.dumps( + { + "type": "http-agent", + "team": "Platform", + "capabilities": [ + "current weather", + "5-day forecast", + "severe weather alerts", + ], + "endpoint": "https://api.example.com/weather", + } + ) + } + }, + }, + { + "name": "order_status_agent", + "description": "Tracks order status, shipping updates, and estimated delivery times for e-commerce orders. Integrates with major carriers like UPS, FedEx, and USPS.", + "descriptorType": "CUSTOM", + "descriptors": { + "custom": { + "inlineContent": json.dumps( + { + "type": "http-agent", + "team": "Commerce", + "capabilities": [ + "order tracking", + "shipping status", + "delivery estimates", + "return status", + ], + "endpoint": "https://api.example.com/orders", + } + ) + } + }, + }, + { + "name": "customer_support_agent", + "description": "Handles customer inquiries, processes refunds, and escalates issues. Uses knowledge base for FAQ resolution and sentiment analysis for prioritization.", + "descriptorType": "CUSTOM", + "descriptors": { + "custom": { + "inlineContent": json.dumps( + { + "type": "http-agent", + "team": "Support", + "capabilities": [ + "FAQ resolution", + "refund processing", + "ticket escalation", + "sentiment analysis", + ], + "endpoint": "https://api.example.com/support", + } + ) + } + }, + }, + { + "name": "inventory_lookup_agent", + "description": "Checks real-time product inventory across warehouses and stores. Supports SKU lookup, stock level alerts, and reorder recommendations.", + "descriptorType": "CUSTOM", + "descriptors": { + "custom": { + "inlineContent": json.dumps( + { + "type": "http-agent", + "team": "Supply Chain", + "capabilities": [ + "stock levels", + "warehouse lookup", + "reorder alerts", + "SKU search", + ], + "endpoint": "https://api.example.com/inventory", + } + ) + } + }, + }, +] + + +def seed(registry_id): + """Create records, submit for approval, and approve them. + + Returns list of created record dicts. + """ + cp = _cp_client() + created = [] + for rec in RECORDS: + logger.info("Creating record '%s' (%s)...", rec["name"], rec["descriptorType"]) + try: + resp = cp.create_registry_record(registryId=registry_id, **rec) + record_id = resp["recordArn"].split("/")[-1] + logger.info(" Created %s", record_id) + created.append({"name": rec["name"], "recordId": record_id}) + except cp.exceptions.ConflictException: + logger.info(" Already exists — skipping") + except Exception as e: + logger.error(" Failed: %s", e) + + if created: + logger.info("Approving %d record(s)...", len(created)) + time.sleep(2) + for rec in created: + try: + cp.submit_registry_record_for_approval( + registryId=registry_id, + recordId=rec["recordId"], + ) + cp.update_registry_record_status( + registryId=registry_id, + recordId=rec["recordId"], + status="APPROVED", + statusReason="Auto-seed", + ) + logger.info(" ✓ %s approved", rec["name"]) + except Exception as e: + logger.error(" ✗ %s: %s", rec["name"], e) + + logger.info("Done — seeded %d record(s)", len(created)) + return created + + +def delete_registry(registry_id): + """Delete all records in a registry, then delete the registry itself.""" + cp = _cp_client() + records = cp.list_registry_records(registryId=registry_id).get( + "registryRecords", [] + ) + for rec in records: + rid = rec["recordId"] + logger.info("Deleting record %s...", rid) + cp.delete_registry_record(registryId=registry_id, recordId=rid) + logger.info("Deleting registry %s...", registry_id) + resp = cp.delete_registry(registryId=registry_id) + logger.info("Registry %s status: %s", registry_id, resp.get("status")) + return resp + + +if __name__ == "__main__": + seed()