Skip to content
Back to skills

Openmetadata Ops

ASecurity

Administer OpenMetadata platform including users, teams, bots, roles, policies, and security. Use when managing access control, configuring SSO, creating service accounts, setting up team hierarchies, or troubleshooting authentication issues.

  • 8 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added June 2, 2026
devopspythongobashsqldockerkubernetesawsazureapidatabase

Works with

  • cli
  • api

Security analysis

A100/100

Scanned June 2, 2026

npx -y skills add aRustyDev/agents --skill openmetadata-ops --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Openmetadata Ops?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Openmetadata Ops
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/arustydev-openmetadata-ops/badge)](https://www.skillsdirectory.com/skills/arustydev-openmetadata-ops)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: openmetadata-ops
description: Administer OpenMetadata platform including users, teams, bots, roles, policies, and security. Use when managing access control, configuring SSO, creating service accounts, setting up team hierarchies, or troubleshooting authentication issues.
---

# OpenMetadata Operations

Guide for administering OpenMetadata platform: users, teams, bots, roles, policies, security configuration, and operational tasks.

## When to Use This Skill

- Managing users and team hierarchies
- Creating and configuring bots (service accounts)
- Setting up roles and policies (RBAC/ABAC)
- Configuring SSO and authentication providers
- Managing JWT tokens and API access
- Troubleshooting authentication and authorization issues

## This Skill Does NOT Cover

- Building OpenMetadata SDKs (see `openmetadata-sdk-dev`)
- Using SDKs/APIs to build integrations (see `openmetadata-dev`)
- Deploying OpenMetadata infrastructure (Kubernetes, Docker)
- Database administration for OpenMetadata backend

---

## Team Hierarchy

### Team Types

OpenMetadata uses a hierarchical team structure:

```
Organization (root)
├── Business Unit
│   ├── Division
│   │   ├── Department
│   │   │   └── Group ← Only Groups can own Data Assets
│   │   └── Group
│   └── Department
│       └── Group
└── Business Unit
    └── Group
```

| Level | Purpose | Can Own Assets |
|-------|---------|----------------|
| **Organization** | Company root, auto-created | No |
| **Business Unit** | Top-tier organizational unit | No |
| **Division** | Mid-tier under Business Unit | No |
| **Department** | Team under Division | No |
| **Group** | Lowest level, contains users | **Yes** |

### Create Team

**Via UI:**
1. Navigate to **Settings → Teams**
2. Click **Add Team**
3. Select team type (BusinessUnit, Division, Department, Group)
4. Set parent team
5. Add team name, display name, description

**Via API:**
```bash
curl -X PUT "http://localhost:8585/api/v1/teams" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "data-engineering",
    "displayName": "Data Engineering",
    "description": "Data platform team",
    "teamType": "Group",
    "parents": [{"id": "parent-team-uuid", "type": "team"}]
  }'
```

### Team Policies

Assign policies to teams for inherited permissions:

```bash
curl -X PATCH "http://localhost:8585/api/v1/teams/${TEAM_ID}" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json-patch+json" \
  -d '[
    {
      "op": "add",
      "path": "/policies/-",
      "value": {"id": "policy-uuid", "type": "policy"}
    }
  ]'
```

---

## User Management

### Add User

**Via UI:**
1. Navigate to **Settings → Users**
2. Click **Add User**
3. Enter email, name, select teams
4. Assign roles

**Via API:**
```bash
curl -X PUT "http://localhost:8585/api/v1/users" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "john.doe",
    "email": "john.doe@company.com",
    "displayName": "John Doe",
    "teams": [{"id": "team-uuid", "type": "team"}],
    "roles": [{"id": "role-uuid", "type": "role"}]
  }'
```

### Assign Role to User

```bash
curl -X PATCH "http://localhost:8585/api/v1/users/${USER_ID}" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json-patch+json" \
  -d '[
    {
      "op": "add",
      "path": "/roles/-",
      "value": {"id": "data-steward-role-uuid", "type": "role"}
    }
  ]'
```

### Generate User Access Token

Users can generate personal access tokens:

1. Go to **Profile → Access Token**
2. Click **Generate New Token**
3. Set expiration (optional)
4. Copy token immediately (shown only once)

---

## Bots (Service Accounts)

### What Are Bots?

Bots are service accounts for automated processes:
- Ingestion pipelines
- External integrations
- Scheduled jobs
- CI/CD automation

### Built-in Bots

| Bot | Purpose | Default Role |
|-----|---------|--------------|
| `ingestion-bot` | Metadata ingestion pipelines | Ingestion Bot Role |
| `automator-bot` | Automation workflows | Automator Role |

### Create Custom Bot

**Via UI:**
1. Navigate to **Settings → Integrations → Bots**
2. Click **Add Bot**
3. Enter bot name and description
4. Select or create role
5. Copy generated JWT token

**Via API:**
```bash
# Create bot
curl -X PUT "http://localhost:8585/api/v1/bots" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ci-cd-bot",
    "displayName": "CI/CD Bot",
    "description": "Bot for CI/CD pipeline metadata updates",
    "botUser": {
      "name": "ci-cd-bot",
      "email": "ci-cd-bot@openmetadata.org",
      "isBot": true
    }
  }'
```

### Get Bot JWT Token

**Via UI:**
1. Navigate to **Settings → Integrations → Bots**
2. Select the bot
3. Click **Revoke & Regenerate Token** or view existing token

**Via API:**
```bash
# Get bot details including auth mechanism
curl "http://localhost:8585/api/v1/bots/name/ingestion-bot?fields=botUser" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}"
```

### Bot Token Validation

Validate bot tokens at [jwt.io](https://jwt.io) or programmatically:

```python
import jwt

def validate_bot_token(token: str, public_key: str) -> dict:
    """Validate and decode bot JWT token."""
    try:
        payload = jwt.decode(
            token,
            public_key,
            algorithms=["RS256"],
            issuer="open-metadata.org",
        )
        assert payload.get("isBot") == True
        return payload
    except jwt.ExpiredSignatureError:
        raise ValueError("Token expired - regenerate in UI")
    except jwt.InvalidTokenError as e:
        raise ValueError(f"Invalid token: {e}")
```

### Bot Token Rotation

Rotate bot tokens periodically:

1. Navigate to **Settings → Integrations → Bots**
2. Select the bot
3. Click **Revoke & Regenerate Token**
4. Update all systems using the old token
5. Verify ingestion pipelines are working

---

## Roles and Policies

### Access Control Model

OpenMetadata uses hybrid RBAC + ABAC:

```
Authorization Decision = f(User, Resource, Operation)

Where:
- User    = Identity + Roles + Teams
- Resource = Entity Type + Attributes (owner, domain, tags)
- Operation = Create, Edit, Delete, ViewAll, EditOwner, etc.
```

### Built-in Roles

| Role | Description | Key Permissions |
|------|-------------|-----------------|
| **Admin** | Full platform access | All operations on all resources |
| **Data Consumer** | Read-only access | ViewBasic on most entities |
| **Data Steward** | Governance operations | Edit descriptions, tags, glossary |
| **Ingestion Bot Role** | Pipeline operations | Create/edit services and entities |

### Create Custom Role

**Via UI:**
1. Navigate to **Settings → Roles**
2. Click **Add Role**
3. Enter name and description
4. Add policies to the role

**Via API:**
```bash
curl -X PUT "http://localhost:8585/api/v1/roles" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "DataEngineer",
    "displayName": "Data Engineer",
    "description": "Role for data engineering team",
    "policies": [
      {"id": "policy-uuid-1", "type": "policy"},
      {"id": "policy-uuid-2", "type": "policy"}
    ]
  }'
```

### Policy Structure

Policies contain rules that define permissions:

```json
{
  "name": "TableEditPolicy",
  "rules": [
    {
      "name": "AllowEditTables",
      "resources": ["table"],
      "operations": ["Create", "Edit", "Delete"],
      "effect": "allow",
      "condition": "isOwner()"
    }
  ]
}
```

### Common Policy Rules

| Rule | Effect | Description |
|------|--------|-------------|
| `isOwner()` | Condition | User/team owns the resource |
| `inTeam('team-name')` | Condition | User belongs to team |
| `hasDomain('domain')` | Condition | Resource is in domain |
| `hasTag('tag-fqn')` | Condition | Resource has specific tag |

### Create Policy

```bash
curl -X PUT "http://localhost:8585/api/v1/policies" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "TeamOwnershipPolicy",
    "displayName": "Team Ownership Policy",
    "description": "Allow team members to edit owned resources",
    "rules": [
      {
        "name": "EditOwnedResources",
        "resources": ["table", "dashboard", "pipeline"],
        "operations": ["Edit", "EditDescription", "EditTags"],
        "effect": "allow",
        "condition": "isOwner()"
      }
    ]
  }'
```

### Operations Reference

| Operation | Description |
|-----------|-------------|
| `Create` | Create new entities |
| `Delete` | Delete entities |
| `ViewAll` | View all entity fields including sensitive data |
| `ViewBasic` | View basic fields (name, description, tags) |
| `Edit` | Edit entity properties |
| `EditDescription` | Edit description only |
| `EditTags` | Edit tags and classifications |
| `EditOwner` | Change entity owner |
| `EditLineage` | Modify lineage edges |
| `EditCustomFields` | Modify custom properties |

---

## Authentication Configuration

### Supported Providers

| Provider | Type | Notes |
|----------|------|-------|
| OpenMetadata (built-in) | JWT | Development only |
| Google | OIDC | Google Workspace SSO |
| Okta | OIDC | Okta SSO |
| Azure AD | OIDC | Microsoft Entra ID |
| Auth0 | OIDC | Auth0 SSO |
| AWS Cognito | OIDC | AWS Cognito User Pools |
| OneLogin | OIDC | OneLogin SSO |
| Keycloak | OIDC | Self-hosted OIDC |
| Custom OIDC | OIDC | Any OIDC-compliant provider |

> **Note**: OpenMetadata does not support multiple auth providers simultaneously.

### Configure SSO (Example: Okta)

**1. Create Okta Application:**
- Sign in to Okta Admin Console
- Navigate to **Applications → Create App Integration**
- Select **OIDC - OpenID Connect** and **Web Application**
- Set redirect URI: `http://localhost:8585/callback`

**2. Configure OpenMetadata:**

```yaml
# openmetadata.yaml
authenticationConfiguration:
  provider: okta
  publicKeyUrls:
    - https://your-domain.okta.com/oauth2/default/v1/keys
  authority: https://your-domain.okta.com/oauth2/default
  clientId: <your-client-id>
  callbackUrl: http://localhost:8585/callback
```

**3. Configure Frontend:**

```yaml
# openmetadata.yaml (continued)
authorizerConfiguration:
  className: org.openmetadata.service.security.DefaultAuthorizer
  containerRequestFilter: org.openmetadata.service.security.JwtFilter
  adminPrincipals:
    - admin@company.com
  principalDomain: company.com
```

### LDAP/Active Directory

For LDAP integration, use an OIDC proxy like Keycloak:

1. Deploy Keycloak
2. Configure LDAP User Federation in Keycloak
3. Configure OpenMetadata to use Keycloak as OIDC provider

---

## Custom Properties Administration

### Create Custom Property Type

**Via UI:**
1. Navigate to **Settings → Custom Properties**
2. Select entity type (Table, Dashboard, etc.)
3. Click **Add Property**
4. Configure name, type, description

**Via API:**
```bash
curl -X PUT "http://localhost:8585/api/v1/metadata/types/name/table/customProperties" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "dataClassification",
    "description": "Data classification level",
    "propertyType": {
      "id": "enum-type-uuid",
      "type": "type"
    },
    "customPropertyConfig": {
      "config": {
        "values": ["Public", "Internal", "Confidential", "Restricted"]
      }
    }
  }'
```

### List Custom Properties

```bash
curl "http://localhost:8585/api/v1/metadata/types/name/table?fields=customProperties" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}"
```

---

## Troubleshooting

### Authentication Issues

**Problem: 401 Unauthorized**

1. Check JWT token validity:
   ```bash
   # Decode token at jwt.io or:
   echo $JWT_TOKEN | cut -d. -f2 | base64 -d | jq .
   ```

2. Verify token expiration (`exp` claim)

3. Check issuer matches configuration

**Problem: Bot ingestion failing**

1. Verify bot has `Ingestion Bot Role`:
   ```bash
   curl "http://localhost:8585/api/v1/bots/name/ingestion-bot?fields=botUser" \
     -H "Authorization: Bearer ${ADMIN_TOKEN}"
   ```

2. Check role policies weren't modified

3. Regenerate bot token if expired

### Authorization Issues

**Problem: 403 Forbidden**

1. Check user's roles:
   ```bash
   curl "http://localhost:8585/api/v1/users/name/john.doe?fields=roles,teams" \
     -H "Authorization: Bearer ${ADMIN_TOKEN}"
   ```

2. Verify role has required policy

3. Check policy conditions (isOwner, inTeam, etc.)

**Problem: User can't see entities**

1. Verify `ViewBasic` or `ViewAll` permission
2. Check team hierarchy - user must be in a Group
3. Verify entity isn't soft-deleted

### Common Fixes

| Issue | Solution |
|-------|----------|
| Token expired | Regenerate token in UI |
| Missing permissions | Add policy to role |
| User not in team | Add user to Group-type team |
| Bot not working | Check role assignment, regenerate token |
| SSO login failing | Verify redirect URI, check OIDC config |

---

## Operational Tasks

### Backup Considerations

Critical data to back up:
- Database (MySQL/PostgreSQL)
- Elasticsearch indices
- JWT signing keys
- Configuration files

### Audit Logging

OpenMetadata tracks changes via:
- Entity version history
- Change events in Elasticsearch
- API audit logs

Query audit events:
```bash
curl "http://localhost:8585/api/v1/events?entityType=table&timestamp=1704067200000" \
  -H "Authorization: Bearer ${ADMIN_TOKEN}"
```

### Health Checks

```bash
# API health
curl "http://localhost:8585/api/v1/system/health"

# Elasticsearch health
curl "http://localhost:8585/api/v1/search/health"
```

---

## Best Practices

### Security

1. **Never use built-in auth in production** - always configure SSO
2. **Rotate bot tokens** periodically (every 90 days recommended)
3. **Use least-privilege roles** - create specific policies per team
4. **Audit admin access** regularly

### Team Structure

1. **Use Groups for ownership** - only Groups can own assets
2. **Mirror org structure** - Business Unit → Division → Department → Group
3. **Assign domain owners** - use domains for cross-team governance

### Bot Management

1. **Create purpose-specific bots** - don't reuse ingestion-bot
2. **Document bot purposes** in descriptions
3. **Monitor bot token usage** in audit logs
4. **Disable unused bots** rather than deleting

---

## References

- [OpenMetadata Security Guide](https://docs.open-metadata.org/latest/deployment/security)
- [Roles and Policies](https://docs.open-metadata.org/latest/how-to-guides/admin-guide-roles-policies)
- [Team Management](https://docs.open-metadata.org/latest/how-to-guides/admin-guide/teams-and-users)
- [SSO Configuration](https://docs.open-metadata.org/latest/deployment/security)
- `openmetadata-dev` - Using SDKs/APIs for integrations
- `openmetadata-sdk-dev` - Implementing SDKs for new languages

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…