| name | evaluate-o11y-permissions |
| description | Use when the user asks whether their credentials/API key has the right permissions, roles, or privileges to export from their source or to import dashboards / create alert rules into Kibana, or wants to check access before committing to a migration — verifies the credentials have what an obs-migrate migration needs end-to-end — read/export on the source (Grafana/Datadog) and write on the Elastic/Kibana target. |
Evaluate migration permissions (source + target)
Audience: operators of the published obs-migrate CLI (PyPI/uvx), using public docs and their real source + Elastic/Kibana — not a repo lab harness.
Goal: give the user confidence their credentials can perform every step before they invest in a migration. Separate non-mutating probes from checks that change target state, and be honest about what each proves.
Prerequisites (install)
These skills help operators of the published CLI (not a repo checkout).
If obs-migrate is missing or doctor is not Ready, follow
install-obs-migrate first — that skill owns PyPI/uvx/pip, extras, and
Python/uv gotchas. Do not invent alternate install commands here.
uvx --from 'elastic-observability-migration[all]' obs-migrate doctor
Source/Elastic credentials: connect-to-o11y-source (and your env exports).
Mental model (state this to the user)
- The source (Grafana/Datadog) is read-only. The tool never writes back to the source. So the only source permission that matters is read/search/export of dashboards (and, for Datadog, monitors). If
connect-to-o11y-source succeeded, source read is already proven.
- The target (Elastic/Kibana) is where write permission matters. The migration needs an API key that can:
- create/update dashboards via the typed Dashboards API —
PUT /api/dashboards/{id} (default obs-migrate upload / migrate --upload path)
- read saved objects / list dashboards —
obs-migrate cluster list-dashboards (Serverless uses _export)
- manage data views —
GET/POST/DELETE /api/data_views/...
- create alert rules (only if migrating alerts) —
POST /api/alerting/rule and GET /api/alerting/rules/_find (creation fails closed unless it can inventory existing migrated rules)
- read target indices for field validation — ES
_field_caps
- delete migrated alert rules —
obs-migrate delete-rules (dry-run by default; --confirm to delete)
Source permission check (non-mutating)
Reading dashboards is the proof. (See the connect-to-o11y-source skill for full setup.)
export GRAFANA_URL="https://grafana.example.com" GRAFANA_USER="..." GRAFANA_PASS="..."
KIBANA_URL= obs-migrate migrate \
--source grafana --input-mode api \
--output-dir /tmp/perm-src --assets dashboards
- Pulled dashboards: source read permission is sufficient.
- 401/403: the source user/token lacks read access (or is wrong).
Note on Grafana alerts: Grafana alert artifacts are derived from dashboard JSON during migration, not fetched as a separate API asset. Do not treat --assets alerts as a distinct source permission probe for Grafana. For Datadog, monitor read is a real separate scope — --assets alerts with --source datadog exercises the Monitors API.
--assets takes exactly one value: dashboards, alerts, or all. It is not a comma list — to exercise both dashboard and monitor reads in one Datadog run use --assets all, not --assets dashboards,alerts.
Target permission checks — non-mutating first
These do not create or modify dashboards/rules. Run these to validate the Kibana API key safely:
Export your target endpoints/key first (any names work; this skill uses KIBANA_ENDPOINT, ELASTICSEARCH_ENDPOINT, KEY):
export KIBANA_ENDPOINT="https://...kb..." ELASTICSEARCH_ENDPOINT="https://...es..." KEY="<api-key>"
obs-migrate cluster detect-serverless --kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY"
obs-migrate cluster list-dashboards --kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY"
curl -sf -H "Authorization: ApiKey $KEY" "$ELASTICSEARCH_ENDPOINT/metrics-*/_field_caps?fields=*" >/dev/null && echo "ES read OK"
obs-migrate audit-rules --kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY"
audit-rules is read-only by default (it only lists migrated rules; it disables nothing unless you pass --disable-enabled). On a target with no migrated rules yet it simply reports zero — that still proves the key can reach and read the Alerting API. (Raw equivalent if you prefer: curl -s -H "Authorization: ApiKey $KEY" "$KIBANA_ENDPOINT/api/alerting/_health".)
Custom-CA / self-signed targets: every obs-migrate command above (cluster ..., audit-rules, upload, verify-alert-rules) accepts the global TLS flags --ca-cert <bundle> (env OBS_MIGRATE_CA_CERT) and --insecure (env OBS_MIGRATE_INSECURE). If a probe fails with a TLS/CERTIFICATE_VERIFY_FAILED error rather than a 401/403, that's a trust problem, not a permission gap — add --ca-cert (keeps verification on; preferred) or, only with explicit user consent, --insecure. For the raw curl field-caps check, the analogous escapes are curl --cacert <bundle> or curl -k.
ensure-data-views creates/updates data views, so treat it as a mutating check:
obs-migrate cluster ensure-data-views \
--kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY" \
--data-view-patterns "metrics-*,logs-*"
Target write proof — these CHANGE target state
Only run these when the user accepts that they create objects. State this explicitly before running.
obs-migrate upload \
--artifact-dir <their-output-dir>/dashboards \
--kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY"
obs-migrate verify-alert-rules \
--comparison <their-output-dir>/alerts/alert_comparison_results.json \
--kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY" \
--limit 1
verify-alert-rules is the preferred alert write check because it cleans up after itself. If the user has no comparison report yet (no alert migration run), the alternative is obs-migrate migrate --source grafana --input-mode api --output-dir /tmp/perm-alerts --assets alerts --kibana-url "$KIBANA_ENDPOINT" --kibana-api-key "$KEY" --create-alert-rules, which creates rules disabled and tagged obs-migration but does not self-clean — afterward: obs-migrate audit-rules ... --disable-enabled to disable, then obs-migrate delete-rules --kibana-url ... --kibana-api-key ... (dry-run) and ... --confirm to delete. The dashboard upload proof also leaves a dashboard behind — delete it with obs-migrate cluster delete-dashboards if it was only a test.
Serverless caveats (call these out)
- Saved-object
GET/_find/direct DELETE are blocked on Serverless. Listing uses _export; "delete" rewrites objects to [DELETED] placeholders via re-import. So a user can lack nothing and still be unable to hard-delete — that is the platform, not a permission gap.
- Migration-created rules are disabled by default and tagged
obs-migration.
Do NOT
- Do not present a state-changing command (
upload, ensure-data-views, rule creation) as a "safe permission check" without saying it mutates the target.
- Do not invent flags, endpoints, or privilege names.
obs-migrate doctor checks local tool resolution, not credentials/permissions.
- Do not claim Grafana
--assets alerts proves a separate source alert-read permission.
- Do not point package users at
scripts/... files or examples/... YAML for the alert checks — use obs-migrate verify-alert-rules / obs-migrate audit-rules (shipped) and the user's own migrated output instead.
- Do not describe
audit-rules (without --disable-enabled) as mutating — it only reads.
See also
install-obs-migrate — install/doctor when the CLI is missing or not Ready.
connect-to-o11y-source skill — source setup and reachability.
obs-migrate verify-alert-rules --help and obs-migrate audit-rules --help — the self-cleaning alert write proof and the read-only rule audit (shipped in the package).
obs-migrate cluster --help and obs-migrate migrate --help — authoritative target/alerting flags for the installed version.
https://github.com/elastic/observability-migration-platform/blob/main/docs/command-contract.md — cluster actions and the alert upload flow (online docs / repo).