chore: added design-docs for account management api#152
chore: added design-docs for account management api#152christian-kreuzberger-dtx wants to merge 5 commits intomainfrom
Conversation
3e247f4 to
b8208fa
Compare
9a5b042 to
d136516
Compare
d136516 to
d7eae72
Compare
|
|
||
| ```go | ||
| // iamBaseURLForTier returns the IAM service URL for the detected tier | ||
| func iamBaseURLForTier(env Environment) string { |
There was a problem hiding this comment.
We're currently in the process of switching to new authz subdomains for service that exposes /access-info endpoint. We'll keep existing one available for now, but at some point in time we would like to sunset those listed in this function.
Up to date list of new subdomains for this service can be found here: https://dt-rnd.atlassian.net/wiki/spaces/PIET/pages/71338308/Domain+Schema#Authorization-services-domains-(authz)
| dtctl iam get policies --level global | ||
| dtctl iam get bindings --level environment [--level-id <envID>] [--group NAME] | ||
| dtctl iam get boundaries --level environment [--level-id <envID>] | ||
| dtctl iam get environments |
There was a problem hiding this comment.
Not sure if there is a case for making environments subcommand IAM specific from user point of view.
| **dtctl focuses on the policy bindings system** as it is the current platform | ||
| approach. The key concept: | ||
|
|
||
| 1. **Policies** are created at a **level** (`account` or `environment`) and |
There was a problem hiding this comment.
Actually environment level policies are deprecated and aren't fully supported in UI. Users are encouraged to create policies on account level, so that they can be used for every environment within that account.
I would need to double check if we blocked that kind of operation on API level.
There was a problem hiding this comment.
Agree, will update to account level only.
There was a problem hiding this comment.
Updated: default to account-level, allow overrides on environment-level. If one of the calls doesn't work with environment-level, please let me know.
|
|
||
| ### IAM API Endpoints | ||
|
|
||
| All under `https://api.dynatrace.com/iam/v1/accounts/{accountUUID}/`: |
There was a problem hiding this comment.
I wish our API would be consistent with the table below 😅 Or at least just "consistent".
Unfortunately iam/v1/accounts/{accountUUID}/ as prefix doesn't work. Would it be useful to have more explicit reference to OpenAPI spec while working on the design? see: https://api.dynatrace.com/spec-json
There was a problem hiding this comment.
I'll reference the spec and will verify endpoints
| # Uses the same --level as the policy. | ||
| dtctl iam create binding \ | ||
| --policy "event-writer" \ | ||
| --group "Event Writers" \ |
There was a problem hiding this comment.
Is there a pattern in dtctl that users work with friendly names of Dynatrace entities (documents, dashboards, etc.)?
Binding API endpoints definitely require raw group uuid in payload or path, so to make it work with friendly name dtctl would need to do a lookup for group uuid.
There was a problem hiding this comment.
yes, e.g., for Dashboards and for Workflows.
| dtctl iam create binding --policy "viewer" --group "Team" [--boundary "prod"] # account level (default) | ||
| dtctl iam create binding --policy "viewer" --group "Team" --level environment [--level-id <envID>] [--boundary "prod"] | ||
| dtctl iam create boundary --name "prod" --zones "Production,Staging" # account level (default) | ||
| dtctl iam create boundary --name "prod" --zones "Production,Staging" --level environment [--level-id <envID>] |
There was a problem hiding this comment.
--zones sounds wrong, both as a parameter name and suggested content of that parameter.
I think that this was meant as a parameter similar to --statement for policies. I'd go for something like --conditions, because that's what you put into boundary definition. You can get some samples here:
- https://juno.internal.dynatrace.com/docs/dt-platform/component/policy-resolution/policies_with_boundaries_resolution/#examples
- https://docs.dynatrace.com/docs/manage/identity-access-management/permission-management/manage-user-permissions-policies/iam-policy-boundaries#boundaries-and-default-policies
| |--------|-----------|------------|---------| | ||
| | **IAM** | `/iam/v1/accounts/{uuid}/...` | `account-idm-read/write` | Users, groups, policies, bindings, boundaries | | ||
| | **Subscriptions** | `/sub/v2/accounts/{uuid}/...` | `account-uac-read` | DPS subscriptions, usage, cost | | ||
| | **Cost allocation** | `/v1/accounts/{uuid}/settings/costcenters`, `/v1/accounts/{uuid}/settings/products` | `account-uac-read` | Cost center and product field values | |
There was a problem hiding this comment.
| | **Cost allocation** | `/v1/accounts/{uuid}/settings/costcenters`, `/v1/accounts/{uuid}/settings/products` | `account-uac-read` | Cost center and product field values | | |
| | **Cost allocation** | `/v1/accounts/{uuid}/settings/costcenters`, `/v1/accounts/{uuid}/settings/products` | `account-uac-read` | Costcenter and product field values | |
The API path uses costcenters (one word). This rename should be applied throughout:
- L76: "Costcenter and product CRUD"
- L1060:
dtctl account get costcenters - L1228: "Costcenters/products" in pagination table
- L1249:
GetCostcentersin package layout - L1408: "costcenter/product write operations"
- L1425: "costcenters, products" in scope table
- L1432: "costcenter/product management"
- L1484: "Costcenter/product management"
- L1538: "List costcenters" in appendix
There was a problem hiding this comment.
Why would we not just call it "Cost center"? That's also the official wording in Dynatrace Docs.
There was a problem hiding this comment.
Oh, my bad, seems like this got renamed in the docs, somehow remembered that "costcenters" was used everywhere ... its just important that the query-parameters + url-paths are always written as one word.
| # Note: subscription-level `dtctl account get usage` (without --per-environment) is | ||
| # deferred to Phase 2a pending API time-filter support (see Phase 2). | ||
| dtctl account get cost --subscription <uuid> [--granularity daily|weekly|monthly] | ||
| dtctl account get cost --subscription <uuid> --per-environment --from 2026-01-01 --to 2026-03-31 |
There was a problem hiding this comment.
Replace lines 1038--1043 with:
# Usage & Cost
dtctl account get usage [--subscription <uuid>] [--capability <key>]
dtctl account get usage --per-environment [--subscription <uuid>] [--from <date>] [--to <date>] [--env <id>] [--capability <key>] [--cluster <id>]
dtctl account get cost [--subscription <uuid>] [--capability <key>]
dtctl account get cost --per-environment [--subscription <uuid>] [--from <date>] [--to <date>] [--env <id>] [--capability <key>] [--cluster <id>]Ship both subscription-level and per-environment in Phase 1. Subscription-level is pass-through — no --granularity, no --from/--to (the API doesn't support time filters on these endpoints).
dtctl has zero precedent for client-side aggregation — all existing resource handlers are strict pass-through. Adding domain-specific rollup logic would break the "No Leaky Abstractions" principle from API_DESIGN.md.
Having dtctl account get usage --per-environment work but not dtctl account get usage would also be a UX problem — users expect the broad command before narrowing.
Also adds missing --cluster <id> to get cost --per-environment — the v3 cost endpoint supports clusterIds (Managed only) just like usage.
There was a problem hiding this comment.
An alternative worth considering: defer all 4 cost/usage commands to Phase 2 (both subscription-level and per-environment). This avoids the warning UX and keeps Phase 1 focused on subscriptions, forecast, and rate-card.
The tradeoff:
- Option A (current suggestion): Ship per-environment (server-side filters) + subscription-level (pass-through with warning) in Phase 1. Full cost/usage coverage from day one, but the subscription-level warning is a bit noisy.
- Option B: Defer all 4 to Phase 2, ship them together once the API has
startTime/endTime. Cleaner UX, no warnings, but Phase 1 has no cost/usage commands at all.
Both options avoid client-side aggregation.
| dtctl account get products | ||
| dtctl account get cost-allocation --subscription <uuid> --env <id> --field COSTCENTER|PRODUCT [--from <date>] [--to <date>] | ||
| # Note: --env is REQUIRED (no account-wide mode in Phase 1); --from/--to default to current month. | ||
| ``` |
There was a problem hiding this comment.
Replace lines 1059--1064 with:
# Cost Allocation (Phase 3)
dtctl account get costcenters
dtctl account get products
dtctl account get cost-allocation --subscription <uuid> --env <id> --field COSTCENTER|PRODUCT [--from <date>] [--to <date>]
# --env is REQUIRED; --from/--to default to current month. See Cost Allocation Guardrails.Rename cost-centers → costcenters. Mark as Phase 3. Simplify env note — constraint is permanent.
| | Get usage | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/usage` | 2a | | ||
| | Get usage/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/usage` | 2a | | ||
| | Get cost | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/cost` | 1 | | ||
| | Get cost/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/cost` | 1 | |
There was a problem hiding this comment.
Replace lines 1097--1100 with:
| Get usage | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/usage` | 1 |
| Get usage/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/usage` | 1 |
| Get cost | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/cost` | 1 |
| Get cost/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/cost` | 1 |Move usage to Phase 1 (pass-through). Note: the latest PR already fixed the v3 path for usage/env — good.
| dtctl adds `--granularity daily|weekly|monthly` (default `monthly`) and | ||
| performs the rollup client-side before printing. This is safe because the | ||
| response size is bounded -- subscriptions are capped at 1 year, so the | ||
| daily record count is bounded at roughly 365. |
There was a problem hiding this comment.
Replace lines 1112--1119 with:
**Subscription-level cost and usage (pass-through with warning).** Both
subscription-level endpoints return the full subscription history in a single GET
with no server-side time filter or aggregation. dtctl passes the response through
as-is (no client-side rollup or trimming) but warns on stderr:
Warning: subscription-level usage returns the full history (1+ MB).
Use --per-environment with --from/--to for narrower results,
or --capability <key> to filter server-side.
The cost endpoint response is small enough (~50 KB) that no warning is needed.
The usage warning is triggered unconditionally on the subscription-level path.
When the API adds `startTime`/`endTime` parameters, dtctl will expose them as
`--from`/`--to` passthrough flags (see Phase 2a).
> **Design note:** dtctl follows a pass-through design — resource handlers return
> API responses without client-side aggregation or transformation. Adding
> `--granularity` rollup or `--from`/`--to` client-side trimming would be the only
> instance of domain-specific business logic in the CLI. See API_DESIGN.md,
> "No Leaky Abstractions" rule.Replace --granularity client-side rollup with pass-through + stderr warning. The design note should live in the doc itself so future contributors understand why we chose not to aggregate client-side.
| subscription history is always returned, which makes it unsafe to ship as | ||
| a CLI default. When the API adds time filtering, the subscription-level | ||
| `UsageHandler` methods are added and `dtctl account get usage` (without | ||
| `--per-environment`) is enabled. |
There was a problem hiding this comment.
Replace lines 1391--1399 with:
**Phase 2a (waiting on API time filters).** Subscription-level cost and usage
currently download the full history (cost: ~50 KB, usage: 1+ MB) because the API
lacks `startTime`/`endTime` parameters. Phase 1 ships these as pass-through with
a stderr warning on usage. When the API adds server-side time filtering, dtctl
exposes `--from`/`--to` as passthrough flags on both subscription-level commands,
eliminating the unnecessary data transfer and the warning.Phase 2a is now an optimization, not a blocker.
| - `account get cost-allocation` (per-environment, required `--env`) | ||
| - Effective permissions analysis (uses `iam:effective-permissions:read` | ||
| scope, added in this phase only) | ||
| - (Optional) cost center/product write operations |
There was a problem hiding this comment.
Replace lines 1404--1408 with:
- `account get costcenters`, `get products`, `get cost-allocation`
(per-environment, required `--env`; see Cost Allocation Guardrails for
page-cap behavior and scale limitations pending API granularity support)
- `CostcentersHandler`, `ProductHandler`, `CostAllocationHandler`
- Effective permissions analysis (uses `iam:effective-permissions:read`
scope, added in this phase only)
- (Optional) costcenter/product write operationsMove costcenters/products/cost-allocation to Phase 3.
| | Get usage | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/usage` | No | 2a (needs `startTime`/`endTime`) | | ||
| | Get usage/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/usage` | Yes (cursor) | 2a (needs `startTime`/`endTime`) | | ||
| | Get cost | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/cost` | No | 1 | | ||
| | Get cost/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/cost` | Yes (cursor) | 1 | |
There was a problem hiding this comment.
Replace lines 1527--1530 with:
| Get usage | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/usage` | No | 1 (pass-through, warns on large response) |
| Get usage/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/usage` | Yes (cursor) | 1 |
| Get cost | GET | `/sub/v2/accounts/{uuid}/subscriptions/{subUuid}/cost` | No | 1 (pass-through) |
| Get cost/env | GET | `/sub/v3/accounts/{uuid}/subscriptions/{subUuid}/environments/cost` | Yes (cursor) | 1 |Move to Phase 1 as pass-through.
| | Access info (IAM svc) | GET | `{iamBaseURL}/api/public/environment-access/access-info` | No | 1 | | ||
| | Get cost allocation | GET | `/v1/accounts/{uuid}/settings/costcenters` (per field) | Yes (cursor) | 3 | | ||
| | List cost centers | GET | `/v1/accounts/{uuid}/settings/costcenters` | Yes (offset) | 1 | | ||
| | List products | GET | `/v1/accounts/{uuid}/settings/products` | Yes (offset) | 1 | |
There was a problem hiding this comment.
Replace lines 1538--1539 with:
| List costcenters | GET | `/v1/accounts/{uuid}/settings/costcenters` | Yes (offset) | 3 |
| List products | GET | `/v1/accounts/{uuid}/settings/products` | Yes (offset) | 3 |Move to Phase 3.
| auto-discovery must be disabled for client-credentials contexts and an | ||
| explicit `account-uuid` required instead. | ||
|
|
||
| 3. **Rate limiting.** The account management API may have stricter rate limits |
There was a problem hiding this comment.
current configuration:
/iam/.*endpoints: 600 requests/min per user- all others: 200 requests/min per user
Description
New design docs for
dtctl iamanddtctl account, based on https://github.com/dynatrace-oss/dtctl/blob/docs/account-namespace-design/docs/dev/ACCOUNT_NAMESPACE_DESIGN.md from @discostu105Related Issues
Closes #
Testing
make test)make lint)