diff --git a/.cursor/skills/author-generation-rules/SKILL.md b/.cursor/skills/author-generation-rules/SKILL.md new file mode 100644 index 00000000..a1cb032d --- /dev/null +++ b/.cursor/skills/author-generation-rules/SKILL.md @@ -0,0 +1,27 @@ +--- +name: author-generation-rules +description: "Write generation rules using bundled references in this repo (airgap). Full skill body lives in runwhen-platform-mcp." +--- + +# Author Generation Rules (runwhen-local) + +Canonical sources: `docs/authoring/` and generated catalogs in this repo. + +For **airgap MCP**, use the bundled copy in `runwhen-platform-mcp`: + +`skills/author-generation-rules/references/` + +Regenerate MCP bundle (maintainers): + +```bash +cd runwhen-platform-mcp +python scripts/sync_bundled_authoring.py --runwhen-local /path/to/runwhen-local +``` + +Regenerate catalogs here first: + +```bash +python scripts/*/dump_*_resource_catalog.py +``` + +See `runwhen-platform-mcp/skills/author-generation-rules/SKILL.md` for the full workflow. diff --git a/.github/workflows/publish-discovery-catalog.yaml b/.github/workflows/publish-discovery-catalog.yaml new file mode 100644 index 00000000..8bdbc786 --- /dev/null +++ b/.github/workflows/publish-discovery-catalog.yaml @@ -0,0 +1,57 @@ +name: Publish discovery catalogs + +on: + workflow_dispatch: + pull_request: + paths: + - "src/indexers/**" + - "src/enrichers/runwhen_platform.py" + - "scripts/**/dump_*_resource_catalog.py" + - "scripts/**/sync_*_resource_type_registry.py" + push: + branches: [main] + paths: + - "src/indexers/**" + - "src/enrichers/runwhen_platform.py" + - "scripts/**/dump_*_resource_catalog.py" + - "scripts/**/sync_*_resource_type_registry.py" + +permissions: + contents: write + +jobs: + regenerate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref || github.ref_name }} + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install PyYAML + run: pip install pyyaml + + - name: Run catalog dumpers + run: | + python scripts/azure/dump_azure_resource_catalog.py + python scripts/aws/dump_aws_resource_catalog.py + python scripts/gcp/dump_gcp_resource_catalog.py + python scripts/kubernetes/dump_kubernetes_resource_catalog.py + python scripts/runwhen/dump_runwhen_resource_catalog.py + + - name: Commit and push regenerated catalogs + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add docs/authoring/indexed-resources/*-resource-catalog.md + if git diff --staged --quiet; then + echo "Catalogs already up to date" + exit 0 + fi + git commit -m "chore(docs): regenerate discovery resource catalogs" + git push diff --git a/docs/authoring/README.md b/docs/authoring/README.md index 05576853..5722f193 100644 --- a/docs/authoring/README.md +++ b/docs/authoring/README.md @@ -3,43 +3,52 @@ Everything you need to extend RunWhen Local: write CodeBundles, ship Skills, and teach the workspace builder how to wire them up via generation rules. +**This directory is the canonical reference.** Catalogs are generated from indexer +registries (dump scripts + `publish-discovery-catalog` GHA). + +- **MCP (airgap):** `author-generation-rules` skill reads bundled copies in + `runwhen-platform-mcp/skills/author-generation-rules/references/` (CI-synced from here) +- **docs.runwhen.com:** use `publish-author-docs-from-runwhen-local` skill in the + docs repo when ready to replace legacy `/authors/generation-rules/` pages + If you only want to *use* RunWhen Local against your own infrastructure, see the [user guide](../user-guide/README.md) instead. ## Concepts -* [CodeBundle / Skill / SLX / Runbook terminology](./concepts.md) - read this - first; the rest of the authoring docs assume you know what these mean. +* [CodeBundle / Skill / SLX / Runbook terminology](./concepts.md) ## Indexed resources Generation rules match against resources discovered by RunWhen Local's -indexers. Before you can write a rule that targets, say, an Azure App Service -or a Kubernetes Deployment, you need to know: +indexers. Before you write a rule, confirm the resource type exists and +which fields are stable enough to match on. -* Whether the indexer actually discovers that resource type today. -* What the data looks like once it's been normalized. -* Which fields are stable enough to match against. +* [Indexed resources overview](./indexed-resources/README.md) +* [Azure](./indexed-resources/azure.md) — typed vs generic tiers, match properties +* [AWS](./indexed-resources/aws.md) +* [GCP](./indexed-resources/gcp.md) +* [Kubernetes](./indexed-resources/kubernetes.md) +* [RunWhen platform](./indexed-resources/runwhen-platform.md) — `platform: runwhen` for MCP tool-builder output -Reference docs per platform: +Machine-readable catalogs (regenerate with `scripts/*/dump_*_resource_catalog.py` +or the `publish-discovery-catalog` GitHub Action — do not hand-edit): -* [Indexed resources overview](./indexed-resources/README.md) -* [Azure indexer](./indexed-resources/azure.md) - 25 typed resource types, - with the data shape your generation rules will see for each. -* [Kubernetes indexer](./indexed-resources/kubernetes.md) -* [AWS indexer](./indexed-resources/aws.md) -* [GCP indexer](./indexed-resources/gcp.md) +* [Azure catalog](./indexed-resources/azure-resource-catalog.md) +* [AWS catalog](./indexed-resources/aws-resource-catalog.md) +* [GCP catalog](./indexed-resources/gcp-resource-catalog.md) +* [Kubernetes catalog](./indexed-resources/kubernetes-resource-catalog.md) +* [RunWhen catalog](./indexed-resources/runwhen-platform-resource-catalog.md) ## Generation rules -Generation rules are the bridge between an indexed resource and a rendered -SLX. They live in CodeBundles under `.runwhen/generation-rules/`. - -* [Generation rules: schema, lifecycle, and how-to](./generation-rules/README.md) -* [Tag-hierarchy contract](./generation-rules/tag-hierarchy-contract.md) - - how SLX names are composed from the resource graph. -* Worked examples: - * [Azure VM + disk runbook](./generation-rules/examples/azure-vm-disk-runbook.md) - * [Azure Key Vault SLX](./generation-rules/examples/azure-keyvault-slx.md) - * [Kubernetes Deployment SLX](./generation-rules/examples/kubernetes-deployment-slx.md) - * [Multi-resource runbook](./generation-rules/examples/multi-resource-runbook.md) +* [Schema reference](./generation-rules/README.md) +* [Syntax reference](../../generation-rules-guide.md) (repo root) — full `matchRules` / `slxs` / `outputItems` +* [Tag-hierarchy contract](./generation-rules/tag-hierarchy-contract.md) +* [Worked examples](./generation-rules/examples/) + +## Publishing to docs.runwhen.com (later) + +Use the **`publish-author-docs-from-runwhen-local`** skill in the `runwhen/docs` +repo (runs `scripts/publish-author-docs-from-runwhen-local.py` against this tree). +Replace legacy `/authors/generation-rules/*` — do not duplicate. diff --git a/docs/authoring/generation-rules/README.md b/docs/authoring/generation-rules/README.md index 6130d6f0..df191e62 100644 --- a/docs/authoring/generation-rules/README.md +++ b/docs/authoring/generation-rules/README.md @@ -1,138 +1,156 @@ # Generation rules -Generation rules are the bridge between an indexed resource and a rendered -SLX. Each rule is a YAML document that lives inside a CodeBundle under -`.runwhen/generation-rules/.yaml` and tells RunWhen Local: +Generation rules are YAML documents inside a CodeCollection under +`.runwhen/generation-rules/*.yaml`. They tell RunWhen Local which indexed +resources to match and which SLX templates to render for each match. -1. Which resource type to match (e.g. `azure_keyvault_keyvaults`). -2. Which subset of those resources to match (predicates over their - attributes / tags / hierarchy). -3. Which template files to render into the SLX (runbook, SLI, SLO, etc.). -4. How to name the resulting SLX. +Each file uses the **`GenerationRules`** kind (note the plural): one platform +per file, with one or more rule blocks under `spec.generationRules`. -This page is the reference; for end-to-end examples see -[examples/](./examples/). +For the full reference with match-rule operators, qualifiers, and complete +examples, see [`generation-rules-guide.md`](../../../generation-rules-guide.md) +at the repo root. Worked narratives live under [examples/](./examples/). ## Schema ```yaml # .runwhen/generation-rules/.yaml apiVersion: runwhen.com/v1 -kind: GenerationRule +kind: GenerationRules spec: - match: - resource_type: azure_keyvault_keyvaults # required - predicates: # optional, ALL must pass - - jsonpath: $.tags.environment - in: ["prod", "staging"] - - jsonpath: $.properties.publicNetworkAccess - equals: "Disabled" - - slxName: - template: "azure-keyvault-{{ resource.name }}-rotation" - # or: - # tagHierarchy: env/region/{resource.name} - # see ./tag-hierarchy-contract.md for the full hierarchy contract. - - templates: - runbook: runbook.robot.j2 - sli: sli.yaml.j2 - slo: slo.yaml.j2 - skill: SKILL.md # optional, copied verbatim if present - - context: # values exposed to the templates as `{{ ... }}` - keyVaultId: "{{ resource.id }}" - keyVaultName: "{{ resource.name }}" - resourceGroup: "{{ resource.resource_group }}" - subscription: "{{ resource.subscription_id }}" - rotationDays: 90 + platform: azure # azure | aws | gcp | kubernetes | runwhen + generationRules: + - resourceTypes: + - azure_keyvault_vaults + - resource_group + matchRules: + - type: pattern + pattern: "prod" + properties: [name] + mode: substring + - type: exists + path: "resource/tags/environment" + matchEmpty: false + slxs: + - baseName: keyvault-health + qualifiers: ["resource", "resource_group", "subscription_id"] + baseTemplateName: azure-keyvault-health + levelOfDetail: detailed + outputItems: + - type: slx + - type: sli + - type: runbook + templateName: azure-keyvault-health-taskset.yaml ``` -### `match.resource_type` +### `spec.platform` + +Selects which indexer supplies matched resources. Supported values: + +| Platform | Indexer | Typical `resourceTypes` values | +| --- | --- | --- | +| `azure` | `azureapi` | CloudQuery table names (`azure_compute_virtual_machines`, aliases like `virtual_machine`) | +| `aws` | `awsapi` | CloudQuery table names (`aws_ec2_instances`, aliases like `ec2_instance`) | +| `gcp` | `gcpapi` | CloudQuery table names (`gcp_compute_instances`, aliases like `compute_instance`) | +| `kubernetes` | `kubeapi` | Built-in kinds (`pod`, `deployment`) or CRD syntax (`buckets.storage.gcp.upbound.io`) | +| `runwhen` | `runwhen` | `workspace` (one instance per workspace-builder run; used by MCP tool-builder output) | -Must be the canonical name (or an alias) of an indexed resource type. See -[indexed-resources/](../indexed-resources/README.md) for the per-platform -catalog. Aliases are resolved by the registry, so all of -`virtual_machine`, `azure_compute_virtual_machines`, and -`azure_keyvault_vaults` resolve to the same underlying type. +See [indexed-resources/](../indexed-resources/README.md) for per-platform +narrative guides and machine-readable catalogs. -### `match.predicates` +### `resourceTypes` -Each predicate is a `(jsonpath, op, value)` triple. Supported ops: +List of resource type names to match. Names are resolved through each +platform's registry — canonical CloudQuery table names and legacy aliases +both work (for example `virtual_machine` and +`azure_compute_virtual_machines` resolve to the same Azure type). + +After discovery, confirm types and field paths in the **Workspace Explorer** +at [http://localhost:8000/explorer/](http://localhost:8000/explorer/) (not +legacy `resource-dump.yaml` on disk). + +For Kubernetes CRDs, use `plural.group[/version]` syntax as shown in +[examples/](./examples/). -| Op | Semantics | +### `matchRules` + +Predicates over matched resources. **All** rules in the list must pass +(logical AND). Supported rule types include: + +| Type | Purpose | | --- | --- | -| `equals` | Exact match (case-sensitive). | -| `not_equals` | Negation of `equals`. | -| `in` | Value is in a list. | -| `not_in` | Value is not in a list. | -| `matches` | Regex match (Python `re.search`). | -| `exists` | The path resolves to a non-`None` value. | -| `greater_than` / `less_than` | Numeric comparison. | -| `present` / `absent` | Tag-shape predicate (`tags.` is/isn't set). | +| `pattern` | Regex match on built-in properties (`name`) or paths (`resource/tags/env`) | +| `exists` | Path resolves to a non-empty value | +| `custom-variable` | Match a workspace custom variable | +| `and` / `or` / `not` | Compound logic | + +See [`generation-rules-guide.md`](../../../generation-rules-guide.md#match-rules) +for operators (`exact`, `substring`), path notation, and compound examples. -All predicates must pass for the rule to fire (logical AND). For OR / -NOT logic, write multiple rules or combine with `not_in` etc. +### `slxs` -### `slxName` +Each entry describes one SLX (or SLX family when `qualifiers` expand names) +to render for every resource that passes `matchRules`. -The SLX name has to be globally unique per workspace. Two strategies: +| Field | Required | Description | +| --- | --- | --- | +| `baseName` | yes | Stable prefix for generated SLX names | +| `baseTemplateName` | yes | Template bundle under `.runwhen/templates/` | +| `qualifiers` | no | Extra name segments (`resource`, `namespace`, `region`, …) | +| `levelOfDetail` | no | `basic` or `detailed` (platform-dependent) | +| `outputItems` | no | Which artifacts to render (`slx`, `sli`, `runbook`, …) | -* `template` - a Jinja-style string referencing the matched - `resource`. Must produce a stable, DNS-friendly slug. -* `tagHierarchy` - delegate naming to the - [tag-hierarchy contract](./tag-hierarchy-contract.md). Useful when you - want SLXs grouped by `env/team/cluster/...`. +Template files live under `.runwhen/templates/-*.yaml` +(for example `azure-keyvault-health-sli.yaml`, +`azure-keyvault-health-taskset.yaml`). Runbooks historically use the +`*-taskset.yaml` suffix. -### `templates` +If a `SKILL.md` sits at the CodeCollection root, the workspace builder +copies it into each rendered SLX directory automatically. -Paths are resolved **relative to the CodeBundle root**, not the rule -file. `runbook` is required; `sli`, `slo`, `skill` are optional. Any -template marked here is rendered with the rule's `context` plus the -matched `resource` dict. +### `outputItems` -If a `SKILL.md` (any case) sits at the CodeBundle root, the workspace -builder copies it next to the rendered SLX even if you don't list it -under `templates`. This is what makes the AI-agent-readable Skill -overlay automatic. +Controls which template artifacts are emitted for an `slxs` entry. Common +types: -### `context` +```yaml +outputItems: + - type: slx + - type: sli + - type: runbook + templateName: my-runbook-taskset.yaml # optional override +``` -A flat dict of values made available to the rendered templates. Values -can reference the matched `resource` via `{{ resource. }}`. -Good practice is to expose every ID / name / region your runbook needs -via `context`, so the runbook itself stays free of resource-store-specific -plumbing. +You can also define top-level `outputItems` on a generation rule block for +direct template rendering without going through `slxs` — see the guide for +advanced cases. ## Lifecycle -1. The workspace builder runs the indexer for each configured platform. -2. For every `(CodeBundle, generation-rule)` pair it walks the matching - resources in the resource store. -3. For each match it renders the templates into - `output/slx//`, copies any `SKILL.md`, and emits an entry - in the SLX manifest. -4. The explorer UI reads the manifest at runtime; the platform upload - path can ship the same artifacts to the connected RunWhen Platform. +1. RunWhen Local runs each configured platform indexer and stores resources. +2. For each CodeCollection, the workspace builder loads every + `.runwhen/generation-rules/*.yaml` file for the matching `platform`. +3. For each `(rule, resource)` pair where `resourceTypes` and `matchRules` + pass, it renders templates into the workspace output tree. +4. The explorer UI and platform upload path consume the resulting SLXs. ## Authoring tips -* **Match against stable fields.** ARM IDs and `resource_type` don't - change; tags and statuses do. Predicates over `properties.*` are fine, - but expect occasional flakiness if the cloud provider mutates a - status field unexpectedly. -* **Keep `context` flat.** Templates rendered with deeply nested - contexts are harder to debug than ones that read a flat keyspace. -* **Test with dry-run.** Run the workspace builder with - `--verbose` and inspect `output/` before shipping the CodeBundle. -* **One rule per "shape" of SLX.** If an SLX template only makes sense - for production resources, write a `prod-only` rule rather than a - `template if env == prod`. It's easier to read and easier to disable. +* **Pick types from the catalog.** Use the + [indexed resource catalogs](../indexed-resources/README.md) instead of + guessing CloudQuery table names. +* **Match on stable fields.** IDs and type names change less often than + status properties. +* **One rule per SLX shape.** Prefer separate rules for prod vs. non-prod + rather than conditional logic inside templates. +* **Dry-run locally.** Run the workspace builder with verbose logging and + inspect `output/` before publishing the CodeCollection. ## See also -* [Worked examples](./examples/) - four end-to-end rules. -* [Tag-hierarchy contract](./tag-hierarchy-contract.md). -* [Indexed resources](../indexed-resources/README.md) - the catalog of - matchable types. -* [Concepts](../concepts.md) - CodeBundle / Skill / SLX terminology. +* [`generation-rules-guide.md`](../../../generation-rules-guide.md) — full + syntax reference +* [Worked examples](./examples/) — end-to-end narratives +* [Tag-hierarchy contract](./tag-hierarchy-contract.md) +* [Indexed resources](../indexed-resources/README.md) — matchable type catalogs +* [Concepts](../concepts.md) — CodeCollection / Skill / SLX terminology diff --git a/docs/authoring/generation-rules/examples/README.md b/docs/authoring/generation-rules/examples/README.md index 53c161a7..4ef1a1c9 100644 --- a/docs/authoring/generation-rules/examples/README.md +++ b/docs/authoring/generation-rules/examples/README.md @@ -9,6 +9,6 @@ output. Use them as starting points for your own CodeBundles. | [Azure VM + disk runbook](./azure-vm-disk-runbook.md) | Single resource type, predicate over tags. | | [Azure Key Vault SLX](./azure-keyvault-slx.md) | Single resource type with a `SKILL.md` overlay. | | [Kubernetes Deployment SLX](./kubernetes-deployment-slx.md) | Predicates over `metadata` + `spec`, namespace-scoped. | -| [Multi-resource runbook](./multi-resource-runbook.md) | A rule that bundles several related resources into one SLX. | +| [Multi-resource context](./multi-resource-runbook.md) | Pass related ARM IDs from one matched resource into templates. | For the schema reference, see [../README.md](../README.md). diff --git a/docs/authoring/generation-rules/examples/azure-keyvault-slx.md b/docs/authoring/generation-rules/examples/azure-keyvault-slx.md index ed0b91ed..ce330de8 100644 --- a/docs/authoring/generation-rules/examples/azure-keyvault-slx.md +++ b/docs/authoring/generation-rules/examples/azure-keyvault-slx.md @@ -6,13 +6,13 @@ AI agent. ## Matched resource -`azure_keyvault_keyvaults` with `properties.publicNetworkAccess == -"Disabled"`: +`azure_keyvault_vaults` (alias: `azure_keyvault_keyvaults`) with +`properties.publicNetworkAccess == "Disabled"`: ```yaml id: /subscriptions/abc/resourceGroups/rg-prod/providers/Microsoft.KeyVault/vaults/kv-prod-001 name: kv-prod-001 -resource_type: azure_keyvault_keyvaults +resource_type: azure_keyvault_vaults resource_group: rg-prod subscription_id: abc location: eastus @@ -31,87 +31,82 @@ properties: ``` codebundles/azure-keyvault-rotation/ -├── SKILL.md # AI-agent-readable Skill description -├── runbook.robot.j2 # rendered into each SLX -├── sli.yaml.j2 -├── slo.yaml.j2 -└── .runwhen/generation-rules/ - └── private-keyvault-rotation.yaml # the rule below +├── SKILL.md +└── .runwhen/ + ├── generation-rules/ + │ └── private-keyvault-rotation.yaml + └── templates/ + ├── azure-keyvault-rotation-slx.yaml + ├── azure-keyvault-rotation-sli.yaml + └── azure-keyvault-rotation-taskset.yaml ``` ## Generation rule ```yaml apiVersion: runwhen.com/v1 -kind: GenerationRule +kind: GenerationRules spec: - match: - resource_type: azure_keyvault_keyvaults - predicates: - - jsonpath: $.properties.publicNetworkAccess - equals: "Disabled" - - slxName: - template: "azure-keyvault-{{ resource.name }}-rotation" - - templates: - runbook: runbook.robot.j2 - sli: sli.yaml.j2 - slo: slo.yaml.j2 - # No 'skill:' line is needed - the workspace builder auto-copies - # SKILL.md from the CodeBundle root into every rendered SLX. - - context: - keyVaultName: "{{ resource.name }}" - keyVaultId: "{{ resource.id }}" - vaultUri: "{{ resource.properties.vaultUri }}" - resourceGroup: "{{ resource.resource_group }}" - subscription: "{{ resource.subscription_id }}" - rotationDays: 90 + platform: azure + generationRules: + - resourceTypes: + - azure_keyvault_vaults + matchRules: + - type: pattern + pattern: "Disabled" + properties: ["resource/properties/publicNetworkAccess"] + mode: exact + slxs: + - baseName: az-kv-rotation + qualifiers: ["resource", "resource_group", "subscription_id"] + baseTemplateName: azure-keyvault-rotation + levelOfDetail: detailed + outputItems: + - type: slx + - type: sli + - type: runbook + templateName: azure-keyvault-rotation-taskset.yaml ``` -## SKILL.md (excerpt) +Place `SKILL.md` at the CodeBundle root — the workspace builder copies it +into every rendered SLX directory automatically (no `outputItems` entry +required). -```markdown -# Azure Key Vault rotation +## Template variables -This Skill rotates secrets older than the configured threshold in a -single Azure Key Vault. It assumes the running identity has -`Microsoft.KeyVault/vaults/secrets/setSecret/action` permission on the -target vault. +In Jinja2 templates under `.runwhen/templates/`, use `match_resource` for +resource fields: -## Inputs -- `keyVaultName` (string, required) -- `vaultUri` (URL, required) -- `rotationDays` (int, default 90) - -## Side effects -- Generates new secret versions for any secret whose current version is - older than `rotationDays`. -- Old versions are kept (not purged) so a rollback path remains. +```yaml +# excerpt from azure-keyvault-rotation-taskset.yaml +spec: + configProvided: + - name: KEY_VAULT_NAME + value: {{ match_resource.name }} + - name: VAULT_URI + value: {{ match_resource.resource.properties.vaultUri }} + - name: RESOURCE_GROUP + value: {{ resource_group.name }} ``` -When the workspace builder fires this rule for `kv-prod-001` it -renders: +## Rendered output + +For `kv-prod-001` in resource group `rg-prod`: ``` -output/slx/azure-keyvault-kv-prod-001-rotation/ +output/slx/az-kv-rotation--rg-prod--kv-prod-001--/ ├── SKILL.md # auto-copied from CodeBundle root -├── runbook.robot +├── slx.yaml ├── sli.yaml -└── slo.yaml +└── taskset.yaml ``` -The explorer UI shows the `SKILL.md` next to the runbook; an MCP-aware -agent can read it as the canonical description of what the SLX *does*. +The explorer UI shows `SKILL.md` next to the runbook; MCP agents can read +it as the canonical description of what the SLX does. ## Notes -* The `publicNetworkAccess` predicate scopes the rule to private vaults - only; vaults that allow public network access get a different rule (or - no SLX at all). -* `vaultUri` is exposed in the context because the runbook uses the - data-plane URL, not the ARM ID, when calling the Key Vault REST API. -* If you also want a separate SLX for *public* vaults, write a sibling - rule with `predicates: - jsonpath: $.properties.publicNetworkAccess - not_equals: "Disabled"`. +* The `publicNetworkAccess` match scopes the rule to private vaults only. +* Use path notation (`resource/properties/...`) to match nested ARM fields. +* For public vaults, add a sibling rule with a complementary pattern or + omit the property match entirely. diff --git a/docs/authoring/generation-rules/examples/azure-vm-disk-runbook.md b/docs/authoring/generation-rules/examples/azure-vm-disk-runbook.md index 13d7f76a..aa9e4dbe 100644 --- a/docs/authoring/generation-rules/examples/azure-vm-disk-runbook.md +++ b/docs/authoring/generation-rules/examples/azure-vm-disk-runbook.md @@ -5,7 +5,7 @@ slow disk" runbook against its OS disk. ## Matched resource -A `azure_compute_virtual_machines` resource with `tags.environment == +An `azure_compute_virtual_machines` resource with `tags.environment == "prod"`. Example payload (trimmed): ```yaml @@ -28,72 +28,65 @@ properties: ## Generation rule -`.runwhen/generation-rules/azure-vm-disk-diagnose.yaml` inside your -CodeBundle: +`.runwhen/generation-rules/azure-vm-disk-diagnose.yaml`: ```yaml apiVersion: runwhen.com/v1 -kind: GenerationRule +kind: GenerationRules spec: - match: - resource_type: azure_compute_virtual_machines - predicates: - - jsonpath: $.tags.environment - equals: "prod" - - jsonpath: $.properties.storageProfile.osDisk.managedDisk.id - exists: true - - slxName: - template: "azure-vm-{{ resource.name }}-disk-health" - - templates: - runbook: runbook.robot.j2 - sli: sli.yaml.j2 - skill: SKILL.md - - context: - vmName: "{{ resource.name }}" - vmId: "{{ resource.id }}" - osDiskId: "{{ resource.properties.storageProfile.osDisk.managedDisk.id }}" - osDiskName: "{{ resource.properties.storageProfile.osDisk.name }}" - resourceGroup: "{{ resource.resource_group }}" - subscription: "{{ resource.subscription_id }}" + platform: azure + generationRules: + - resourceTypes: + - azure_compute_virtual_machines + matchRules: + - type: pattern + pattern: "prod" + properties: [tags] + mode: substring + - type: exists + properties: ["resource/properties/storageProfile/osDisk/managedDisk/id"] + slxs: + - baseName: az-vm-disk-hlth + qualifiers: ["resource", "resource_group", "subscription_id"] + baseTemplateName: azure-vm-disk-diagnose + levelOfDetail: detailed + outputItems: + - type: slx + - type: sli + - type: runbook + templateName: azure-vm-disk-diagnose-taskset.yaml ``` +Add `SKILL.md` at the CodeBundle root if you want MCP-readable metadata; +the workspace builder copies it into each rendered SLX directory. + ## Rendered output -For each matching VM the workspace builder produces a directory like: +For each matching VM: ``` -output/slx/azure-vm-web-01-disk-health/ -├── SKILL.md # copied verbatim from the CodeBundle root -├── runbook.robot # rendered with vmName="web-01", osDiskName="web-01-os", ... -└── sli.yaml +output/slx/az-vm-disk-hlth--rg-prod--web-01--/ +├── SKILL.md # optional, from CodeBundle root +├── slx.yaml +├── sli.yaml +└── taskset.yaml ``` -`runbook.robot.j2` can reference the context directly: +Reference disk fields in templates via `match_resource`: -```robot -*** Settings *** -Documentation Diagnose slow disk on Azure VM ${vmName}. - -*** Variables *** -${VM_ID} {{ vmId }} -${DISK_ID} {{ osDiskId }} -${RESOURCE_GROUP} {{ resourceGroup }} -${SUBSCRIPTION} {{ subscription }} - -*** Tasks *** -Check IOPS on ${osDiskName} - [Documentation] Pulls 24h of disk metrics from Azure Monitor. - ... +```yaml +spec: + configProvided: + - name: VM_NAME + value: {{ match_resource.name }} + - name: OS_DISK_ID + value: {{ match_resource.resource.properties.storageProfile.osDisk.managedDisk.id }} + - name: RESOURCE_GROUP + value: {{ resource_group.name }} ``` ## Notes -* `predicates` here include an `exists` check so we skip VMs that don't - expose a managed-disk OS disk (e.g. ephemeral OS disks). -* The `osDiskId` is enough on its own; we also expose `osDiskName` purely - for human-readable runbook headers. -* `tags.environment` is user-supplied data; if it's missing the predicate - evaluates to false and the SLX isn't generated. +* The `exists` match skips VMs without a managed OS disk (e.g. ephemeral OS). +* Tag predicates use the built-in `tags` property with `substring` mode. +* Missing `tags.environment` causes the rule not to fire for that VM. diff --git a/docs/authoring/generation-rules/examples/kubernetes-deployment-slx.md b/docs/authoring/generation-rules/examples/kubernetes-deployment-slx.md index 80ef5f4e..cf10448a 100644 --- a/docs/authoring/generation-rules/examples/kubernetes-deployment-slx.md +++ b/docs/authoring/generation-rules/examples/kubernetes-deployment-slx.md @@ -1,7 +1,7 @@ # Example: Kubernetes Deployment SLX Generate a "diagnose Deployment rollout" SLX for every multi-replica -Deployment in namespaces tagged for production. +Deployment outside `kube-system`. ## Matched resource @@ -29,60 +29,82 @@ status: status: "True" ``` +## CodeBundle layout + +``` +codebundles/k8s-deployment-rollout/ +└── .runwhen/ + ├── generation-rules/ + │ └── k8s-deployment-rollout.yaml + └── templates/ + ├── k8s-deployment-rollout-slx.yaml + ├── k8s-deployment-rollout-sli.yaml + └── k8s-deployment-rollout-taskset.yaml +``` + ## Generation rule ```yaml apiVersion: runwhen.com/v1 -kind: GenerationRule +kind: GenerationRules spec: - match: - resource_type: deployment - predicates: - - jsonpath: $.spec.replicas - greater_than: 1 - - jsonpath: $.metadata.labels.team - exists: true - - jsonpath: $.metadata.namespace - not_equals: "kube-system" - - slxName: - template: >- - k8s-{{ resource.subscription_id }}-{{ resource.metadata.namespace }}-{{ resource.name }}-rollout - - templates: - runbook: runbook.robot.j2 - sli: sli.yaml.j2 - - context: - cluster: "{{ resource.subscription_id }}" - namespace: "{{ resource.metadata.namespace }}" - deployment: "{{ resource.name }}" - team: "{{ resource.metadata.labels.team }}" - replicas: "{{ resource.spec.replicas }}" + platform: kubernetes + generationRules: + - resourceTypes: + - deployment + matchRules: + - type: pattern + pattern: "^(?!kube-system$).*" + properties: [namespace] + mode: exact + - type: exists + properties: ["resource/metadata/labels/team"] + - type: pattern + pattern: "^[2-9][0-9]*$" + properties: ["resource/spec/replicas"] + mode: exact + slxs: + - baseName: k8s-deploy-rollout + qualifiers: ["cluster", "namespace", "resource"] + baseTemplateName: k8s-deployment-rollout + levelOfDetail: detailed + outputItems: + - type: slx + - type: sli + - type: runbook + templateName: k8s-deployment-rollout-taskset.yaml ``` ## Rendered output -For `checkout-api` in cluster `prod-west` you get: +For `checkout-api` in cluster `prod-west`, namespace `payments`: ``` -output/slx/k8s-prod-west-payments-checkout-api-rollout/ -├── runbook.robot -└── sli.yaml +output/slx/k8s-deploy-rollout--prod-west--payments--checkout-api/ +├── slx.yaml +├── sli.yaml +└── taskset.yaml ``` -`runbook.robot.j2` then has access to `${cluster}`, `${namespace}`, -`${deployment}`, `${team}`, `${replicas}` and can shell out to `kubectl` -or call the Kubernetes API directly. +Templates access `match_resource`, `namespace`, and `cluster` directly: + +```yaml +spec: + configProvided: + - name: DEPLOYMENT + value: {{ match_resource.name }} + - name: NAMESPACE + value: {{ namespace.name }} + - name: CLUSTER + value: {{ cluster.name }} + - name: REPLICAS + value: {{ match_resource.resource.spec.replicas }} +``` ## Notes -* The `subscription_id` field on Kubernetes resources is the cluster - name; the SLX naming template includes it so you don't get name - collisions across multiple clusters. -* The `metadata.labels.team` predicate uses `exists`, which is enough - to require *any* team label without pinning to a specific value. -* A namespace's effective Level of Detail must be `BASIC` or `DETAILED` - for its Deployments to even arrive at this rule. Configure that under - `cloudConfig.kubernetes.contexts[].namespaceLevelOfDetails` - - see [Kubernetes-LOD configuration](../../../architecture/kubernetes-lod/configuration.md). +* On Kubernetes resources, `subscription_id` is the cluster name; include + `cluster` in `qualifiers` to avoid collisions across clusters. +* Namespace Level of Detail must be `basic` or `detailed` for Deployments + to be indexed — configure under + `cloudConfig.kubernetes.contexts[].namespaceLevelOfDetails`. diff --git a/docs/authoring/generation-rules/examples/multi-resource-runbook.md b/docs/authoring/generation-rules/examples/multi-resource-runbook.md index c5fc057d..d55491f8 100644 --- a/docs/authoring/generation-rules/examples/multi-resource-runbook.md +++ b/docs/authoring/generation-rules/examples/multi-resource-runbook.md @@ -1,17 +1,16 @@ -# Example: Multi-resource runbook +# Example: Azure web app with related resource IDs -Bundle related resources into a single SLX. This pattern is useful when -the natural unit of troubleshooting spans more than one resource type. +Pass multiple ARM resource identifiers into one runbook from a **single** +matched resource. This pattern covers the common case where troubleshooting +one resource requires IDs that are already present on its payload (here, the +App Service plan ID embedded in the web app). -The setup: an Azure App Service web app + its server farm (App Service -plan) + the Application Gateway in front of it. We want one SLX per web -app that pulls all three IDs into the runbook context. +For SLX-to-SLX graph relationships across separate generation rules, use +map customization rules in your workspace `workspaceInfo.yaml`. ## Matched resource -The "primary" resource for the rule is `azure_appservice_web_apps`. The -related App Service plan and Application Gateway are looked up via -`relatedResources` (resolved at generation time): +The primary resource is `azure_appservice_web_apps`: ```yaml id: /subscriptions/abc/resourceGroups/rg-prod/providers/Microsoft.Web/sites/checkout-api @@ -32,78 +31,67 @@ tags: ```yaml apiVersion: runwhen.com/v1 -kind: GenerationRule +kind: GenerationRules spec: - match: - resource_type: azure_appservice_web_apps - predicates: - - jsonpath: $.tags.environment - equals: "prod" - - relatedResources: - plan: - resource_type: azure_appservice_plans - where: - # Match the plan whose ARM ID equals this web app's serverFarmId. - idEquals: "{{ resource.properties.serverFarmId }}" - appGateway: - resource_type: azure_network_application_gateways - where: - # Match the appgw whose name matches the 'appgw' tag on the web app. - nameEquals: "{{ resource.tags.appgw }}" + platform: azure + generationRules: + - resourceTypes: + - azure_appservice_web_apps + matchRules: + - type: pattern + pattern: "prod" + properties: [tags] + mode: substring + slxs: + - baseName: az-web-e2e + qualifiers: ["resource", "resource_group", "subscription_id"] + baseTemplateName: azure-web-e2e + levelOfDetail: detailed + outputItems: + - type: slx + - type: sli + - type: runbook + templateName: azure-web-e2e-taskset.yaml +``` - slxName: - template: "azure-webapp-{{ resource.name }}-end-to-end" +## Template: expose related IDs from the primary resource - templates: - runbook: runbook.robot.j2 - sli: sli.yaml.j2 +The runbook receives plan and hostname data from `match_resource` — no +separate lookup step is required: - context: - webAppName: "{{ resource.name }}" - webAppId: "{{ resource.id }}" - hostName: "{{ resource.properties.hostNames[0] }}" - serverFarmId: "{{ resource.properties.serverFarmId }}" - serverFarmName: "{{ related.plan.name }}" - appGatewayId: "{{ related.appGateway.id }}" - appGatewayName: "{{ related.appGateway.name }}" - resourceGroup: "{{ resource.resource_group }}" - subscription: "{{ resource.subscription_id }}" +```yaml +# excerpt from azure-web-e2e-taskset.yaml +spec: + configProvided: + - name: WEB_APP_NAME + value: {{ match_resource.name }} + - name: HOST_NAME + value: {{ match_resource.resource.properties.hostNames[0] }} + - name: SERVER_FARM_ID + value: {{ match_resource.resource.properties.serverFarmId }} + - name: APPGW_TAG + value: {{ match_resource.resource.tags.appgw }} + - name: RESOURCE_GROUP + value: {{ resource_group.name }} ``` +The runbook can then call Azure CLI with `--ids ${SERVER_FARM_ID}`, curl +`${HOST_NAME}`, or look up an Application Gateway by name from +`${APPGW_TAG}`. + ## Rendered output ``` -output/slx/azure-webapp-checkout-api-end-to-end/ -├── runbook.robot -└── sli.yaml -``` - -The runbook can now perform multi-step diagnosis: - -```robot -*** Tasks *** -Check Application Gateway Health - Run az network application-gateway show-backend-health - ... --ids ${appGatewayId} - -Check Web App Availability - Run curl -sS https://${hostName}/health - -Check Server Farm Capacity - Run az appservice plan show --ids ${serverFarmId} +output/slx/az-web-e2e--rg-prod--checkout-api--/ +├── slx.yaml +├── sli.yaml +└── taskset.yaml ``` ## Notes -* `relatedResources` is a separate top-level field from `match`. The - rule fires once per *matched primary*, with each related resource - bound under `related.` for the templates. -* If a related resource isn't found (no matching plan, no matching - appgw), the rule still fires, but the corresponding `related.*` - values are empty. Templates should defensively handle missing related - resources or the rule should add a stricter `predicates` block. -* Cross-resource predicates (e.g. "fire only when *both* the web app - and its appgw have `environment: prod`") are best expressed by - predicates on the primary plus a `where` clause that filters the - related lookup. +* When you need **separate SLXs per resource type** (web app + plan + + gateway), write one generation rule per `resourceTypes` entry and link them + in the workspace map with customization rules. +* Tags like `appgw` are user-supplied; document expected tag shapes in your + CodeBundle README or `SKILL.md`. diff --git a/docs/authoring/indexed-resources/README.md b/docs/authoring/indexed-resources/README.md index 9868edc8..3fd26c88 100644 --- a/docs/authoring/indexed-resources/README.md +++ b/docs/authoring/indexed-resources/README.md @@ -1,55 +1,15 @@ -# Indexed resources +# Indexed resource catalogs -Generation rules can only match against resources that an indexer has -actually written to the resource store. This section is the authoritative -reference for what each indexer discovers and the data shape that arrives -in your generation rules. +Agent- and author-facing lookup tables for generation-rule `resourceTypes` and +`match_resource.*` properties. -## Per-platform reference +| Platform | Narrative guide | Machine catalog | +|---|---|---| +| Kubernetes | [kubernetes.md](./kubernetes.md) | [kubernetes-resource-catalog.md](./kubernetes-resource-catalog.md) | +| Azure | [azure.md](./azure.md) | [azure-resource-catalog.md](./azure-resource-catalog.md) | +| GCP | [gcp.md](./gcp.md) | [gcp-resource-catalog.md](./gcp-resource-catalog.md) | +| AWS | [aws.md](./aws.md) | [aws-resource-catalog.md](./aws-resource-catalog.md) | +| RunWhen | [runwhen-platform.md](./runwhen-platform.md) | [runwhen-platform-resource-catalog.md](./runwhen-platform-resource-catalog.md) | -* [Azure](./azure.md) - native `azure-mgmt-*` SDK indexer (`azureapi`). - Full parity with the legacy CloudQuery Azure plugin: 619 indexable - resource types, 25 typed (rich-payload) plus 594 generic (basic - envelope). Sortable catalog at - [azure-resource-catalog.md](./azure-resource-catalog.md). -* [Kubernetes](./kubernetes.md) - in-cluster scan via the Kubernetes Python - client, plus per-namespace LOD support. -* [AWS](./aws.md) - CloudQuery-backed indexer. -* [GCP](./gcp.md) - CloudQuery-backed indexer. - -## Common resource shape - -Every discovered resource lands in the resource store as a normalized dict -with at least: - -| Field | Type | Notes | -| --- | --- | --- | -| `id` | string | The platform's canonical resource ID (ARM ID for Azure, ARN for AWS, `/api/v1/.../` for Kubernetes, etc.). | -| `name` | string | Human-friendly name. | -| `resource_type` | string | RunWhen Local's canonical type name. Use this in generation rule `match.resource_type`. | -| `subscription_id` / `account_id` / `project_id` | string | Cloud account scope. Always set for cloud platforms. | -| `tags` | dict | Always a dict, may be empty. Cloud-platform user tags. | -| `properties` | dict | Platform-specific payload (preserved verbatim). | - -Indexer-specific fields layer on top of those common ones; per-platform -docs spell out the extras and give example payloads. - -## Selective vs unbounded discovery - -All indexers honor the `defaultLOD` / `resourceGroupLevelOfDetails` -contract from `workspaceInfo.yaml`: - -* `defaultLOD: detailed` (and any non-`none` value) means **unbounded - discovery** - the indexer enumerates the whole subscription / account / - cluster. -* `defaultLOD: none` plus a finite list of "keep this" entries means - **selective discovery** - the indexer scopes its API calls to exactly - those resource groups / namespaces. - -When you're authoring generation rules in a CodeBundle, you don't usually -have to think about this; you just match resources. But it does affect -*which* resources show up at runtime. See the user-facing -[level-of-detail guide](../../user-guide/configuration/level-of-detail.md) -and the architecture-level -[Kubernetes-LOD internals](../../architecture/kubernetes-lod/README.md) for -the full mechanics. +Regenerate catalogs with the dump scripts under `scripts/` or via the +`publish-discovery-catalog` GitHub Action. diff --git a/docs/authoring/indexed-resources/aws-resource-catalog.md b/docs/authoring/indexed-resources/aws-resource-catalog.md new file mode 100644 index 00000000..15a8f9be --- /dev/null +++ b/docs/authoring/indexed-resources/aws-resource-catalog.md @@ -0,0 +1,1133 @@ +# AWS resource catalog + +Every AWS resource type the native `awsapi` indexer can discover. This page is the companion catalog for [`aws.md`](./aws.md); see that page for how to enable the indexer, what data each row carries, and the typed/generic distinction. + +_1119 resource types - 3 typed (rich-payload), 1116 generic (Cloud Control envelope). Generated 2026-07-02 from `src/indexers/aws_resource_type_registry.yaml`._ + +_Regenerate with `python scripts/aws/dump_aws_resource_catalog.py` after touching the registry or overrides; do not hand-edit this file._ + +* `typed` - hand-written `boto3` collector returns a richer payload. +* `generic` - covered by the Cloud Control API catch-all when a CloudFormation type exists; rows without a CFN type are registry-only and skipped by generic discovery. + +| Service | CloudQuery table name | CFN type | Tier | +| --- | --- | --- | --- | +| accessanalyzer | `aws_accessanalyzer_analyzer_archive_rules` | `AWS::AccessAnalyzer::AnalyzerArchiveRule` | generic | +| accessanalyzer | `aws_accessanalyzer_analyzer_findings` | `AWS::AccessAnalyzer::AnalyzerFinding` | generic | +| accessanalyzer | `aws_accessanalyzer_analyzer_findings_v2` | `AWS::AccessAnalyzer::AnalyzerFindingsV2` | generic | +| accessanalyzer | `aws_accessanalyzer_analyzers` | `AWS::AccessAnalyzer::Analyzer` | generic | +| account | `aws_account_alternate_contacts` | `AWS::Account::AlternateContact` | generic | +| account | `aws_account_contacts` | `AWS::Account::Contact` | generic | +| acm | `aws_acm_certificates` | `AWS::CertificateManager::Certificate` | generic | +| acmpca | `aws_acmpca_certificate_authorities` | `AWS::ACMPCA::CertificateAuthority` | generic | +| amp | `aws_amp_rule_groups_namespaces` | `AWS::APS::RuleGroupsNamespace` | generic | +| amp | `aws_amp_workspaces` | `AWS::APS::Workspace` | generic | +| amplify | `aws_amplify_apps` | `AWS::Amplify::App` | generic | +| apigateway | `aws_apigateway_accounts` | `AWS::ApiGateway::Account` | generic | +| apigateway | `aws_apigateway_api_keys` | `AWS::ApiGateway::ApiKey` | generic | +| apigateway | `aws_apigateway_client_certificates` | `AWS::ApiGateway::ClientCertificate` | generic | +| apigateway | `aws_apigateway_domain_name_base_path_mappings` | `AWS::ApiGateway::DomainNameBasePathMapping` | generic | +| apigateway | `aws_apigateway_domain_names` | `AWS::ApiGateway::DomainName` | generic | +| apigateway | `aws_apigateway_rest_api_authorizers` | `AWS::ApiGateway::RestApiAuthorizer` | generic | +| apigateway | `aws_apigateway_rest_api_deployments` | `AWS::ApiGateway::RestApiDeployment` | generic | +| apigateway | `aws_apigateway_rest_api_documentation_parts` | `AWS::ApiGateway::RestApiDocumentationPart` | generic | +| apigateway | `aws_apigateway_rest_api_documentation_versions` | `AWS::ApiGateway::RestApiDocumentationVersion` | generic | +| apigateway | `aws_apigateway_rest_api_gateway_responses` | `AWS::ApiGateway::RestApiGatewayRespons` | generic | +| apigateway | `aws_apigateway_rest_api_models` | `AWS::ApiGateway::RestApiModel` | generic | +| apigateway | `aws_apigateway_rest_api_request_validators` | `AWS::ApiGateway::RestApiRequestValidator` | generic | +| apigateway | `aws_apigateway_rest_api_resource_method_integrations` | `AWS::ApiGateway::RestApiResourceMethodIntegration` | generic | +| apigateway | `aws_apigateway_rest_api_resource_methods` | `AWS::ApiGateway::RestApiResourceMethod` | generic | +| apigateway | `aws_apigateway_rest_api_resources` | `AWS::ApiGateway::RestApiResource` | generic | +| apigateway | `aws_apigateway_rest_api_stages` | `AWS::ApiGateway::RestApiStage` | generic | +| apigateway | `aws_apigateway_rest_apis` | `AWS::ApiGateway::RestApi` | generic | +| apigateway | `aws_apigateway_usage_plan_keys` | `AWS::ApiGateway::UsagePlanKey` | generic | +| apigateway | `aws_apigateway_usage_plans` | `AWS::ApiGateway::UsagePlan` | generic | +| apigateway | `aws_apigateway_vpc_links` | `AWS::ApiGateway::VpcLink` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_authorizers` | `AWS::ApiGatewayV2::ApiAuthorizer` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_deployments` | `AWS::ApiGatewayV2::ApiDeployment` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_integration_responses` | `AWS::ApiGatewayV2::ApiIntegrationRespons` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_integrations` | `AWS::ApiGatewayV2::ApiIntegration` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_models` | `AWS::ApiGatewayV2::ApiModel` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_route_responses` | `AWS::ApiGatewayV2::ApiRouteRespons` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_routes` | `AWS::ApiGatewayV2::ApiRoute` | generic | +| apigatewayv2 | `aws_apigatewayv2_api_stages` | `AWS::ApiGatewayV2::ApiStage` | generic | +| apigatewayv2 | `aws_apigatewayv2_apis` | `AWS::ApiGatewayV2::Api` | generic | +| apigatewayv2 | `aws_apigatewayv2_domain_name_rest_api_mappings` | `AWS::ApiGatewayV2::DomainNameRestApiMapping` | generic | +| apigatewayv2 | `aws_apigatewayv2_domain_names` | `AWS::ApiGatewayV2::DomainName` | generic | +| apigatewayv2 | `aws_apigatewayv2_vpc_links` | `AWS::ApiGatewayV2::VpcLink` | generic | +| appconfig | `aws_appconfig_applications` | `AWS::AppConfig::Application` | generic | +| appconfig | `aws_appconfig_configuration_profiles` | `AWS::AppConfig::ConfigurationProfile` | generic | +| appconfig | `aws_appconfig_deployment_strategies` | `AWS::AppConfig::DeploymentStrategy` | generic | +| appconfig | `aws_appconfig_environments` | `AWS::AppConfig::Environment` | generic | +| appconfig | `aws_appconfig_hosted_configuration_versions` | `AWS::AppConfig::HostedConfigurationVersion` | generic | +| appflow | `aws_appflow_flows` | `AWS::AppFlow::Flow` | generic | +| applicationautoscaling | `aws_applicationautoscaling_policies` | `AWS::ApplicationAutoScaling::Policy` | generic | +| applicationautoscaling | `aws_applicationautoscaling_scalable_targets` | `AWS::ApplicationAutoScaling::ScalableTarget` | generic | +| applicationautoscaling | `aws_applicationautoscaling_scaling_activities` | `AWS::ApplicationAutoScaling::ScalingActivity` | generic | +| applicationautoscaling | `aws_applicationautoscaling_scheduled_actions` | `AWS::ApplicationAutoScaling::ScheduledAction` | generic | +| appmesh | `aws_appmesh_meshes` | `AWS::AppMesh::Mesh` | generic | +| appmesh | `aws_appmesh_virtual_gateways` | `AWS::AppMesh::VirtualGateway` | generic | +| appmesh | `aws_appmesh_virtual_nodes` | `AWS::AppMesh::VirtualNode` | generic | +| appmesh | `aws_appmesh_virtual_routers` | `AWS::AppMesh::VirtualRouter` | generic | +| appmesh | `aws_appmesh_virtual_services` | `AWS::AppMesh::VirtualService` | generic | +| apprunner | `aws_apprunner_auto_scaling_configurations` | `AWS::AppRunner::AutoScalingConfiguration` | generic | +| apprunner | `aws_apprunner_connections` | `AWS::AppRunner::Connection` | generic | +| apprunner | `aws_apprunner_custom_domains` | `AWS::AppRunner::CustomDomain` | generic | +| apprunner | `aws_apprunner_observability_configurations` | `AWS::AppRunner::ObservabilityConfiguration` | generic | +| apprunner | `aws_apprunner_operations` | `AWS::AppRunner::Operation` | generic | +| apprunner | `aws_apprunner_services` | `AWS::AppRunner::Service` | generic | +| apprunner | `aws_apprunner_vpc_connectors` | `AWS::AppRunner::VpcConnector` | generic | +| apprunner | `aws_apprunner_vpc_ingress_connections` | `AWS::AppRunner::VpcIngressConnection` | generic | +| appstream | `aws_appstream_app_blocks` | `AWS::AppStream::AppBlock` | generic | +| appstream | `aws_appstream_application_fleet_associations` | `AWS::AppStream::ApplicationFleetAssociation` | generic | +| appstream | `aws_appstream_applications` | `AWS::AppStream::Application` | generic | +| appstream | `aws_appstream_directory_configs` | `AWS::AppStream::DirectoryConfig` | generic | +| appstream | `aws_appstream_fleets` | `AWS::AppStream::Fleet` | generic | +| appstream | `aws_appstream_image_builders` | `AWS::AppStream::ImageBuilder` | generic | +| appstream | `aws_appstream_images` | `AWS::AppStream::Image` | generic | +| appstream | `aws_appstream_stack_entitlements` | `AWS::AppStream::StackEntitlement` | generic | +| appstream | `aws_appstream_stack_user_associations` | `AWS::AppStream::StackUserAssociation` | generic | +| appstream | `aws_appstream_stacks` | `AWS::AppStream::Stack` | generic | +| appstream | `aws_appstream_usage_report_subscriptions` | `AWS::AppStream::UsageReportSubscription` | generic | +| appstream | `aws_appstream_users` | `AWS::AppStream::User` | generic | +| appsync | `aws_appsync_graphql_apis` | `AWS::AppSync::GraphqlApi` | generic | +| athena | `aws_athena_data_catalog_database_tables` | `AWS::Athena::DataCatalogDatabaseTable` | generic | +| athena | `aws_athena_data_catalog_databases` | `AWS::Athena::DataCatalogDatabas` | generic | +| athena | `aws_athena_data_catalogs` | `AWS::Athena::DataCatalog` | generic | +| athena | `aws_athena_work_group_named_queries` | `AWS::Athena::WorkGroupNamedQuery` | generic | +| athena | `aws_athena_work_group_prepared_statements` | `AWS::Athena::WorkGroupPreparedStatement` | generic | +| athena | `aws_athena_work_group_query_executions` | `AWS::Athena::WorkGroupQueryExecution` | generic | +| athena | `aws_athena_work_groups` | `AWS::Athena::WorkGroup` | generic | +| auditmanager | `aws_auditmanager_assessments` | `AWS::AuditManager::Assessment` | generic | +| autoscaling | `aws_autoscaling_group_lifecycle_hooks` | `AWS::AutoScaling::GroupLifecycleHook` | generic | +| autoscaling | `aws_autoscaling_group_scaling_policies` | `AWS::AutoScaling::GroupScalingPolicy` | generic | +| autoscaling | `aws_autoscaling_groups` | `AWS::AutoScaling::AutoScalingGroup` | generic | +| autoscaling | `aws_autoscaling_launch_configurations` | `AWS::AutoScaling::LaunchConfiguration` | generic | +| autoscaling | `aws_autoscaling_plan_resources` | `AWS::AutoScaling::PlanResource` | generic | +| autoscaling | `aws_autoscaling_plans` | `AWS::AutoScaling::Plan` | generic | +| autoscaling | `aws_autoscaling_scheduled_actions` | `AWS::AutoScaling::ScheduledAction` | generic | +| autoscaling | `aws_autoscaling_warm_pools` | `AWS::AutoScaling::WarmPool` | generic | +| availability | `aws_availability_zones` | `-` | generic | +| backup | `aws_backup_frameworks` | `AWS::Backup::Framework` | generic | +| backup | `aws_backup_global_settings` | `AWS::Backup::GlobalSetting` | generic | +| backup | `aws_backup_jobs` | `AWS::Backup::Job` | generic | +| backup | `aws_backup_plan_selections` | `AWS::Backup::PlanSelection` | generic | +| backup | `aws_backup_plans` | `AWS::Backup::BackupPlan` | generic | +| backup | `aws_backup_protected_resources` | `AWS::Backup::ProtectedResource` | generic | +| backup | `aws_backup_region_settings` | `AWS::Backup::RegionSetting` | generic | +| backup | `aws_backup_report_plans` | `AWS::Backup::ReportPlan` | generic | +| backup | `aws_backup_restore_testing_plans` | `AWS::Backup::RestoreTestingPlan` | generic | +| backup | `aws_backup_restore_testing_selections` | `AWS::Backup::RestoreTestingSelection` | generic | +| backup | `aws_backup_tiering_configurations` | `AWS::Backup::TieringConfiguration` | generic | +| backup | `aws_backup_vault_recovery_points` | `AWS::Backup::VaultRecoveryPoint` | generic | +| backup | `aws_backup_vaults` | `AWS::Backup::BackupVault` | generic | +| backupgateway | `aws_backupgateway_gateways` | `AWS::BackupGateway::Gateway` | generic | +| batch | `aws_batch_compute_environments` | `AWS::Batch::ComputeEnvironment` | generic | +| batch | `aws_batch_job_definitions` | `AWS::Batch::JobDefinition` | generic | +| batch | `aws_batch_job_queues` | `AWS::Batch::JobQueue` | generic | +| batch | `aws_batch_jobs` | `AWS::Batch::Job` | generic | +| bedrock | `aws_bedrock_agent_versions` | `AWS::Bedrock::AgentVersion` | generic | +| bedrock | `aws_bedrock_agents` | `AWS::Bedrock::Agent` | generic | +| bedrock | `aws_bedrock_custom_models` | `AWS::Bedrock::CustomModel` | generic | +| bedrock | `aws_bedrock_evaluation_jobs` | `AWS::Bedrock::EvaluationJob` | generic | +| bedrock | `aws_bedrock_foundation_models` | `AWS::Bedrock::FoundationModel` | generic | +| bedrock | `aws_bedrock_guardrails` | `AWS::Bedrock::Guardrail` | generic | +| bedrock | `aws_bedrock_inference_profiles` | `AWS::Bedrock::InferenceProfile` | generic | +| bedrock | `aws_bedrock_model_copy_jobs` | `AWS::Bedrock::ModelCopyJob` | generic | +| bedrock | `aws_bedrock_model_customization_jobs` | `AWS::Bedrock::ModelCustomizationJob` | generic | +| bedrock | `aws_bedrock_provisioned_model_throughputs` | `AWS::Bedrock::ProvisionedModelThroughput` | generic | +| budgets | `aws_budgets_actions` | `AWS::Budgets::Action` | generic | +| budgets | `aws_budgets_budgets` | `AWS::Budgets::Budget` | generic | +| cloudformation | `aws_cloudformation_stack_instance_resource_drifts` | `AWS::CloudFormation::StackInstanceResourceDrift` | generic | +| cloudformation | `aws_cloudformation_stack_instance_summaries` | `AWS::CloudFormation::StackInstanceSummary` | generic | +| cloudformation | `aws_cloudformation_stack_resources` | `AWS::CloudFormation::StackResource` | generic | +| cloudformation | `aws_cloudformation_stack_set_operation_results` | `AWS::CloudFormation::StackSetOperationResult` | generic | +| cloudformation | `aws_cloudformation_stack_set_operations` | `AWS::CloudFormation::StackSetOperation` | generic | +| cloudformation | `aws_cloudformation_stack_sets` | `AWS::CloudFormation::StackSet` | generic | +| cloudformation | `aws_cloudformation_stack_templates` | `AWS::CloudFormation::StackTemplate` | generic | +| cloudformation | `aws_cloudformation_stacks` | `AWS::CloudFormation::Stack` | generic | +| cloudformation | `aws_cloudformation_template_summaries` | `AWS::CloudFormation::TemplateSummary` | generic | +| cloudfront | `aws_cloudfront_cache_policies` | `AWS::CloudFront::CachePolicy` | generic | +| cloudfront | `aws_cloudfront_distributions` | `AWS::CloudFront::Distribution` | generic | +| cloudfront | `aws_cloudfront_functions` | `AWS::CloudFront::Function` | generic | +| cloudfront | `aws_cloudfront_key_value_stores` | `AWS::CloudFront::KeyValueStore` | generic | +| cloudfront | `aws_cloudfront_origin_access_identities` | `AWS::CloudFront::OriginAccessIdentity` | generic | +| cloudfront | `aws_cloudfront_origin_request_policies` | `AWS::CloudFront::OriginRequestPolicy` | generic | +| cloudfront | `aws_cloudfront_response_headers_policies` | `AWS::CloudFront::ResponseHeadersPolicy` | generic | +| cloudhsmv2 | `aws_cloudhsmv2_backups` | `AWS::CloudHSM::Backup` | generic | +| cloudhsmv2 | `aws_cloudhsmv2_clusters` | `AWS::CloudHSM::Cluster` | generic | +| cloudtrail | `aws_cloudtrail_channels` | `AWS::CloudTrail::Channel` | generic | +| cloudtrail | `aws_cloudtrail_events` | `AWS::CloudTrail::Event` | generic | +| cloudtrail | `aws_cloudtrail_imports` | `AWS::CloudTrail::Import` | generic | +| cloudtrail | `aws_cloudtrail_trail_event_selectors` | `AWS::CloudTrail::TrailEventSelector` | generic | +| cloudtrail | `aws_cloudtrail_trails` | `AWS::CloudTrail::Trail` | generic | +| cloudwatch | `aws_cloudwatch_alarms` | `AWS::CloudWatch::Alarm` | generic | +| cloudwatch | `aws_cloudwatch_metric_data` | `-` | generic | +| cloudwatch | `aws_cloudwatch_metric_statistics` | `-` | generic | +| cloudwatch | `aws_cloudwatch_metric_streams` | `AWS::CloudWatch::MetricStream` | generic | +| cloudwatch | `aws_cloudwatch_metrics` | `-` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_deliveries` | `AWS::Logs::Delivery` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_delivery_destinations` | `AWS::Logs::DeliveryDestination` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_delivery_sources` | `AWS::Logs::DeliverySource` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_log_group_data_protection_policies` | `AWS::Logs::LogGroupDataProtectionPolicy` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_log_group_subscription_filters` | `AWS::Logs::LogGroupSubscriptionFilter` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_log_groups` | `AWS::Logs::LogGroup` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_log_streams` | `AWS::Logs::LogStream` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_metric_filters` | `AWS::Logs::MetricFilter` | generic | +| cloudwatchlogs | `aws_cloudwatchlogs_resource_policies` | `AWS::Logs::ResourcePolicy` | generic | +| codeartifact | `aws_codeartifact_domains` | `AWS::CodeArtifact::Domain` | generic | +| codeartifact | `aws_codeartifact_repositories` | `AWS::CodeArtifact::Repository` | generic | +| codebuild | `aws_codebuild_builds` | `AWS::CodeBuild::Build` | generic | +| codebuild | `aws_codebuild_projects` | `AWS::CodeBuild::Project` | generic | +| codebuild | `aws_codebuild_source_credentials` | `AWS::CodeBuild::SourceCredential` | generic | +| codecommit | `aws_codecommit_repositories` | `AWS::CodeCommit::Repository` | generic | +| codedeploy | `aws_codedeploy_applications` | `AWS::CodeDeploy::Application` | generic | +| codedeploy | `aws_codedeploy_deployment_configs` | `AWS::CodeDeploy::DeploymentConfig` | generic | +| codedeploy | `aws_codedeploy_deployment_groups` | `AWS::CodeDeploy::DeploymentGroup` | generic | +| codedeploy | `aws_codedeploy_deployments` | `AWS::CodeDeploy::Deployment` | generic | +| codegurureviewer | `aws_codegurureviewer_repository_associations` | `AWS::CodeGuruReviewer::RepositoryAssociation` | generic | +| codepipeline | `aws_codepipeline_pipelines` | `AWS::CodePipeline::Pipeline` | generic | +| codepipeline | `aws_codepipeline_webhooks` | `AWS::CodePipeline::Webhook` | generic | +| codestar | `aws_codestar_connections_managed` | `AWS::CodeStarConnections::ConnectionsManaged` | generic | +| cognito | `aws_cognito_identity_pools` | `AWS::Cognito::IdentityPool` | generic | +| cognito | `aws_cognito_user_pool_identity_providers` | `AWS::Cognito::UserPoolIdentityProvider` | generic | +| cognito | `aws_cognito_user_pools` | `AWS::Cognito::UserPool` | generic | +| comprehend | `aws_comprehend_document_classification_jobs` | `AWS::Comprehend::DocumentClassificationJob` | generic | +| comprehend | `aws_comprehend_document_classifiers` | `AWS::Comprehend::DocumentClassifier` | generic | +| comprehend | `aws_comprehend_dominant_language_detection_jobs` | `AWS::Comprehend::DominantLanguageDetectionJob` | generic | +| comprehend | `aws_comprehend_endpoints` | `AWS::Comprehend::Endpoint` | generic | +| comprehend | `aws_comprehend_entities_detection_jobs` | `AWS::Comprehend::EntitiesDetectionJob` | generic | +| comprehend | `aws_comprehend_entity_recognizers` | `AWS::Comprehend::EntityRecognizer` | generic | +| comprehend | `aws_comprehend_events_detection_jobs` | `AWS::Comprehend::EventsDetectionJob` | generic | +| comprehend | `aws_comprehend_flywheel_datasets` | `AWS::Comprehend::FlywheelDataset` | generic | +| comprehend | `aws_comprehend_flywheel_iteration_histories` | `AWS::Comprehend::FlywheelIterationHistory` | generic | +| comprehend | `aws_comprehend_flywheels` | `AWS::Comprehend::Flywheel` | generic | +| comprehend | `aws_comprehend_keyphrases_detection_jobs` | `AWS::Comprehend::KeyphrasesDetectionJob` | generic | +| comprehend | `aws_comprehend_pii_entities_etection_jobs` | `AWS::Comprehend::PiiEntitiesEtectionJob` | generic | +| comprehend | `aws_comprehend_sentiment_detection_jobs` | `AWS::Comprehend::SentimentDetectionJob` | generic | +| comprehend | `aws_comprehend_targeted_sentiment_detection_jobs` | `AWS::Comprehend::TargetedSentimentDetectionJob` | generic | +| comprehend | `aws_comprehend_topics_detection_jobs` | `AWS::Comprehend::TopicsDetectionJob` | generic | +| computeoptimizer | `aws_computeoptimizer_autoscaling_group_recommendations` | `AWS::Computeoptimizer::AutoscalingGroupRecommendation` | generic | +| computeoptimizer | `aws_computeoptimizer_ebs_volume_recommendations` | `AWS::Computeoptimizer::EbsVolumeRecommendation` | generic | +| computeoptimizer | `aws_computeoptimizer_ec2_instance_recommendations` | `AWS::Computeoptimizer::Ec2InstanceRecommendation` | generic | +| computeoptimizer | `aws_computeoptimizer_ecs_service_recommendations` | `AWS::Computeoptimizer::EcsServiceRecommendation` | generic | +| computeoptimizer | `aws_computeoptimizer_enrollment_statuses` | `AWS::Computeoptimizer::EnrollmentStatus` | generic | +| computeoptimizer | `aws_computeoptimizer_lambda_function_recommendations` | `AWS::Computeoptimizer::LambdaFunctionRecommendation` | generic | +| computeoptimizer | `aws_computeoptimizer_rds_database_recommendations` | `AWS::Computeoptimizer::RdsDatabaseRecommendation` | generic | +| computeoptimizerautomation | `aws_computeoptimizerautomation_accounts` | `AWS::Computeoptimizerautomation::Account` | generic | +| config | `aws_config_config_rule_compliance_details` | `AWS::Config::ConfigRuleComplianceDetail` | generic | +| config | `aws_config_config_rule_compliances` | `AWS::Config::ConfigRuleCompliance` | generic | +| config | `aws_config_config_rules` | `AWS::Config::ConfigRule` | generic | +| config | `aws_config_configuration_aggregators` | `AWS::Config::ConfigurationAggregator` | generic | +| config | `aws_config_configuration_recorders` | `AWS::Config::ConfigurationRecorder` | generic | +| config | `aws_config_conformance_pack_rule_compliances` | `AWS::Config::ConformancePackRuleCompliance` | generic | +| config | `aws_config_conformance_packs` | `AWS::Config::ConformancePack` | generic | +| config | `aws_config_delivery_channel_statuses` | `AWS::Config::DeliveryChannelStatus` | generic | +| config | `aws_config_delivery_channels` | `AWS::Config::DeliveryChannel` | generic | +| config | `aws_config_remediation_configurations` | `AWS::Config::RemediationConfiguration` | generic | +| config | `aws_config_retention_configurations` | `AWS::Config::RetentionConfiguration` | generic | +| connect | `aws_connect_agent_queues` | `AWS::Connect::AgentQueue` | generic | +| connect | `aws_connect_agent_statuses` | `AWS::Connect::AgentStatus` | generic | +| connect | `aws_connect_approved_origins` | `AWS::Connect::ApprovedOrigin` | generic | +| connect | `aws_connect_authentication_profiles` | `AWS::Connect::AuthenticationProfile` | generic | +| connect | `aws_connect_contact_evaluations` | `AWS::Connect::ContactEvaluation` | generic | +| connect | `aws_connect_contact_flow_modules` | `AWS::Connect::ContactFlowModule` | generic | +| connect | `aws_connect_contact_references` | `AWS::Connect::ContactReference` | generic | +| connect | `aws_connect_contacts` | `AWS::Connect::Contact` | generic | +| connect | `aws_connect_default_vocabularies` | `AWS::Connect::DefaultVocabulary` | generic | +| connect | `aws_connect_evaluation_form_versions` | `AWS::Connect::EvaluationFormVersion` | generic | +| connect | `aws_connect_evaluation_forms` | `AWS::Connect::EvaluationForm` | generic | +| connect | `aws_connect_flow_associations` | `AWS::Connect::FlowAssociation` | generic | +| connect | `aws_connect_hours_of_operations` | `AWS::Connect::HoursOfOperation` | generic | +| connect | `aws_connect_instance_storage_configs` | `AWS::Connect::InstanceStorageConfig` | generic | +| connect | `aws_connect_instances` | `AWS::Connect::Instance` | generic | +| connect | `aws_connect_integration_associations` | `AWS::Connect::IntegrationAssociation` | generic | +| connect | `aws_connect_lambda_functions` | `AWS::Connect::LambdaFunction` | generic | +| connect | `aws_connect_lex_bots` | `AWS::Connect::LexBot` | generic | +| connect | `aws_connect_lex_bots_v1` | `AWS::Connect::LexBotsV1` | generic | +| connect | `aws_connect_lex_bots_v2` | `AWS::Connect::LexBotsV2` | generic | +| connect | `aws_connect_lex_v1_bots` | `AWS::Connect::LexV1Bot` | generic | +| connect | `aws_connect_lex_v2_bots` | `AWS::Connect::LexV2Bot` | generic | +| connect | `aws_connect_phone_numbers` | `AWS::Connect::PhoneNumber` | generic | +| connect | `aws_connect_prompts` | `AWS::Connect::Prompt` | generic | +| connect | `aws_connect_queue_quick_connects` | `AWS::Connect::QueueQuickConnect` | generic | +| connect | `aws_connect_queues` | `AWS::Connect::Queue` | generic | +| connect | `aws_connect_quick_connects` | `AWS::Connect::QuickConnect` | generic | +| connect | `aws_connect_routing_profile_queues` | `AWS::Connect::RoutingProfileQueue` | generic | +| connect | `aws_connect_routing_profiles` | `AWS::Connect::RoutingProfile` | generic | +| connect | `aws_connect_rules` | `AWS::Connect::Rule` | generic | +| connect | `aws_connect_security_keys` | `AWS::Connect::SecurityKey` | generic | +| connect | `aws_connect_security_profiles` | `AWS::Connect::SecurityProfile` | generic | +| connect | `aws_connect_task_templates` | `AWS::Connect::TaskTemplate` | generic | +| connect | `aws_connect_traffic_distribution_group_users` | `AWS::Connect::TrafficDistributionGroupUser` | generic | +| connect | `aws_connect_traffic_distribution_groups` | `AWS::Connect::TrafficDistributionGroup` | generic | +| connect | `aws_connect_use_cases` | `AWS::Connect::UseCas` | generic | +| connect | `aws_connect_user_hierarchy_groups` | `AWS::Connect::UserHierarchyGroup` | generic | +| connect | `aws_connect_user_proficiencies` | `AWS::Connect::UserProficiency` | generic | +| connect | `aws_connect_users` | `AWS::Connect::User` | generic | +| connect | `aws_connect_view_versions` | `AWS::Connect::ViewVersion` | generic | +| connect | `aws_connect_views` | `AWS::Connect::View` | generic | +| costexplorer | `aws_costexplorer_cost_30d` | `-` | generic | +| costexplorer | `aws_costexplorer_cost_custom` | `-` | generic | +| costexplorer | `aws_costexplorer_cost_forecast_30d` | `-` | generic | +| costexplorer | `aws_costexplorer_reservation_coverages` | `-` | generic | +| costexplorer | `aws_costexplorer_reservation_utilizations` | `-` | generic | +| costoptimizationhub | `aws_costoptimizationhub_recommendations` | `-` | generic | +| datapipeline | `aws_datapipeline_pipelines` | `AWS::DataPipeline::Pipeline` | generic | +| datasync | `aws_datasync_agents` | `AWS::DataSync::Agent` | generic | +| datasync | `aws_datasync_azureblob_locations` | `AWS::DataSync::AzureblobLocation` | generic | +| datasync | `aws_datasync_efs_locations` | `AWS::DataSync::EfsLocation` | generic | +| datasync | `aws_datasync_fsxlustre_locations` | `AWS::DataSync::FsxlustreLocation` | generic | +| datasync | `aws_datasync_fsxontap_locations` | `AWS::DataSync::FsxontapLocation` | generic | +| datasync | `aws_datasync_fsxopenzfs_locations` | `AWS::DataSync::FsxopenzfsLocation` | generic | +| datasync | `aws_datasync_fsxwindows_locations` | `AWS::DataSync::FsxwindowsLocation` | generic | +| datasync | `aws_datasync_hdfs_locations` | `AWS::DataSync::HdfsLocation` | generic | +| datasync | `aws_datasync_locations` | `AWS::DataSync::Location` | generic | +| datasync | `aws_datasync_nfs_locations` | `AWS::DataSync::NfsLocation` | generic | +| datasync | `aws_datasync_objectstorage_locations` | `AWS::DataSync::ObjectstorageLocation` | generic | +| datasync | `aws_datasync_s3_locations` | `AWS::DataSync::S3Location` | generic | +| datasync | `aws_datasync_smb_locations` | `AWS::DataSync::SmbLocation` | generic | +| dax | `aws_dax_clusters` | `AWS::DAX::Cluster` | generic | +| detective | `aws_detective_graph_members` | `AWS::Detective::GraphMember` | generic | +| detective | `aws_detective_graphs` | `AWS::Detective::Graph` | generic | +| devopsguru | `aws_devopsguru_anomalies` | `AWS::DevOpsGuru::Anomaly` | generic | +| devopsguru | `aws_devopsguru_events` | `AWS::DevOpsGuru::Event` | generic | +| devopsguru | `aws_devopsguru_insights` | `AWS::DevOpsGuru::Insight` | generic | +| devopsguru | `aws_devopsguru_log_groups` | `AWS::DevOpsGuru::LogGroup` | generic | +| devopsguru | `aws_devopsguru_monitored_resources` | `AWS::DevOpsGuru::MonitoredResource` | generic | +| devopsguru | `aws_devopsguru_notification_channels` | `AWS::DevOpsGuru::NotificationChannel` | generic | +| devopsguru | `aws_devopsguru_recommendations` | `AWS::DevOpsGuru::Recommendation` | generic | +| directconnect | `aws_directconnect_connections` | `AWS::DirectConnect::Connection` | generic | +| directconnect | `aws_directconnect_gateway_associations` | `AWS::DirectConnect::GatewayAssociation` | generic | +| directconnect | `aws_directconnect_gateway_attachments` | `AWS::DirectConnect::GatewayAttachment` | generic | +| directconnect | `aws_directconnect_gateways` | `AWS::DirectConnect::Gateway` | generic | +| directconnect | `aws_directconnect_lags` | `AWS::DirectConnect::Lag` | generic | +| directconnect | `aws_directconnect_locations` | `AWS::DirectConnect::Location` | generic | +| directconnect | `aws_directconnect_virtual_gateways` | `AWS::DirectConnect::VirtualGateway` | generic | +| directconnect | `aws_directconnect_virtual_interfaces` | `AWS::DirectConnect::VirtualInterface` | generic | +| directoryservice | `aws_directoryservice_directories` | `AWS::DirectoryService::Directory` | generic | +| dlm | `aws_dlm_lifecycle_policies` | `AWS::DLM::LifecyclePolicy` | generic | +| dms | `aws_dms_certificates` | `AWS::DMS::Certificate` | generic | +| dms | `aws_dms_event_subscriptions` | `AWS::DMS::EventSubscription` | generic | +| dms | `aws_dms_replication_instances` | `AWS::DMS::ReplicationInstance` | generic | +| dms | `aws_dms_replication_subnet_groups` | `AWS::DMS::ReplicationSubnetGroup` | generic | +| dms | `aws_dms_replication_tasks` | `AWS::DMS::ReplicationTask` | generic | +| docdb | `aws_docdb_certificates` | `AWS::DocDB::Certificate` | generic | +| docdb | `aws_docdb_cluster_parameter_groups` | `AWS::DocDB::ClusterParameterGroup` | generic | +| docdb | `aws_docdb_cluster_parameters` | `AWS::DocDB::ClusterParameter` | generic | +| docdb | `aws_docdb_cluster_snapshots` | `AWS::DocDB::ClusterSnapshot` | generic | +| docdb | `aws_docdb_clusters` | `AWS::DocDB::DBCluster` | generic | +| docdb | `aws_docdb_engine_versions` | `AWS::DocDB::EngineVersion` | generic | +| docdb | `aws_docdb_event_categories` | `AWS::DocDB::EventCategory` | generic | +| docdb | `aws_docdb_event_subscriptions` | `AWS::DocDB::EventSubscription` | generic | +| docdb | `aws_docdb_events` | `AWS::DocDB::Event` | generic | +| docdb | `aws_docdb_global_clusters` | `AWS::DocDB::GlobalCluster` | generic | +| docdb | `aws_docdb_instances` | `AWS::DocDB::DBInstance` | generic | +| docdb | `aws_docdb_orderable_db_instance_options` | `AWS::DocDB::OrderableDbInstanceOption` | generic | +| docdb | `aws_docdb_pending_maintenance_actions` | `AWS::DocDB::PendingMaintenanceAction` | generic | +| docdb | `aws_docdb_subnet_groups` | `AWS::DocDB::SubnetGroup` | generic | +| dsql | `aws_dsql_cluster_policies` | `AWS::DSQL::ClusterPolicy` | generic | +| dsql | `aws_dsql_clusters` | `AWS::DSQL::Cluster` | generic | +| dynamodb | `aws_dynamodb_backups` | `AWS::DynamoDB::Backup` | generic | +| dynamodb | `aws_dynamodb_exports` | `AWS::DynamoDB::Export` | generic | +| dynamodb | `aws_dynamodb_global_tables` | `AWS::DynamoDB::GlobalTable` | generic | +| dynamodb | `aws_dynamodb_table_continuous_backups` | `AWS::DynamoDB::TableContinuousBackup` | generic | +| dynamodb | `aws_dynamodb_table_replica_auto_scalings` | `AWS::DynamoDB::TableReplicaAutoScaling` | generic | +| dynamodb | `aws_dynamodb_table_resource_policies` | `AWS::DynamoDB::TableResourcePolicy` | generic | +| dynamodb | `aws_dynamodb_table_stream_resource_policies` | `AWS::DynamoDB::TableStreamResourcePolicy` | generic | +| dynamodb | `aws_dynamodb_tables` | `AWS::DynamoDB::Table` | generic | +| dynamodbstreams | `aws_dynamodbstreams_streams` | `AWS::DynamoDB::Stream` | generic | +| ebs | `aws_ebs_default_kms_key_ids` | `AWS::EC2::DefaultKmsKeyId` | generic | +| ebs | `aws_ebs_encryption_by_defaults` | `AWS::EC2::EncryptionByDefault` | generic | +| ec2 | `aws_ec2_account_attributes` | `-` | generic | +| ec2 | `aws_ec2_byoip_cidrs` | `AWS::EC2::ByoipCidr` | generic | +| ec2 | `aws_ec2_capacity_reservation_topologies` | `AWS::EC2::CapacityReservationTopology` | generic | +| ec2 | `aws_ec2_capacity_reservations` | `AWS::EC2::CapacityReservation` | generic | +| ec2 | `aws_ec2_customer_gateways` | `AWS::EC2::CustomerGateway` | generic | +| ec2 | `aws_ec2_dhcp_options` | `AWS::EC2::DHCPOptions` | generic | +| ec2 | `aws_ec2_ebs_snapshot_attributes` | `AWS::EC2::EbsSnapshotAttribute` | generic | +| ec2 | `aws_ec2_ebs_snapshots` | `AWS::EC2::Snapshot` | generic | +| ec2 | `aws_ec2_ebs_volume_statuses` | `AWS::EC2::EbsVolumeStatus` | generic | +| ec2 | `aws_ec2_ebs_volumes` | `AWS::EC2::Volume` | generic | +| ec2 | `aws_ec2_egress_only_internet_gateways` | `AWS::EC2::EgressOnlyInternetGateway` | generic | +| ec2 | `aws_ec2_eips` | `AWS::EC2::EIP` | generic | +| ec2 | `aws_ec2_flow_logs` | `AWS::EC2::FlowLog` | generic | +| ec2 | `aws_ec2_hosts` | `AWS::EC2::Host` | generic | +| ec2 | `aws_ec2_image_block_public_access_states` | `AWS::EC2::ImageBlockPublicAccessState` | generic | +| ec2 | `aws_ec2_image_last_launched_times` | `AWS::EC2::ImageLastLaunchedTime` | generic | +| ec2 | `aws_ec2_image_launch_permissions` | `AWS::EC2::ImageLaunchPermission` | generic | +| ec2 | `aws_ec2_image_references` | `AWS::EC2::ImageReference` | generic | +| ec2 | `aws_ec2_images` | `AWS::EC2::Image` | generic | +| ec2 | `aws_ec2_instance_connect_endpoints` | `AWS::EC2::InstanceConnectEndpoint` | generic | +| ec2 | `aws_ec2_instance_credit_specifications` | `AWS::EC2::InstanceCreditSpecification` | generic | +| ec2 | `aws_ec2_instance_disable_api_stop` | `AWS::EC2::InstanceDisableApiStop` | generic | +| ec2 | `aws_ec2_instance_disable_api_termination` | `AWS::EC2::InstanceDisableApiTermination` | generic | +| ec2 | `aws_ec2_instance_statuses` | `-` | generic | +| ec2 | `aws_ec2_instance_topologies` | `AWS::EC2::InstanceTopology` | generic | +| ec2 | `aws_ec2_instance_types` | `-` | generic | +| ec2 | `aws_ec2_instance_user_data` | `AWS::EC2::InstanceUserData` | generic | +| ec2 | `aws_ec2_instances` | `AWS::EC2::Instance` | typed | +| ec2 | `aws_ec2_internet_gateways` | `AWS::EC2::InternetGateway` | generic | +| ec2 | `aws_ec2_ipam_address_history` | `AWS::EC2::IpamAddressHistory` | generic | +| ec2 | `aws_ec2_ipam_byoasns` | `AWS::EC2::IpamByoasn` | generic | +| ec2 | `aws_ec2_ipam_discovered_accounts` | `AWS::EC2::IpamDiscoveredAccount` | generic | +| ec2 | `aws_ec2_ipam_discovered_public_addresses` | `AWS::EC2::IpamDiscoveredPublicAddress` | generic | +| ec2 | `aws_ec2_ipam_discovered_resource_cidrs` | `AWS::EC2::IpamDiscoveredResourceCidr` | generic | +| ec2 | `aws_ec2_ipam_pool_allocations` | `AWS::EC2::IpamPoolAllocation` | generic | +| ec2 | `aws_ec2_ipam_pool_cidrs` | `AWS::EC2::IpamPoolCidr` | generic | +| ec2 | `aws_ec2_ipam_pools` | `AWS::EC2::IpamPool` | generic | +| ec2 | `aws_ec2_ipam_resource_cidrs` | `AWS::EC2::IpamResourceCidr` | generic | +| ec2 | `aws_ec2_ipam_resource_discoveries` | `AWS::EC2::IpamResourceDiscovery` | generic | +| ec2 | `aws_ec2_ipam_resource_discovery_associations` | `AWS::EC2::IpamResourceDiscoveryAssociation` | generic | +| ec2 | `aws_ec2_ipam_scopes` | `AWS::EC2::IpamScope` | generic | +| ec2 | `aws_ec2_ipams` | `AWS::EC2::Ipam` | generic | +| ec2 | `aws_ec2_key_pairs` | `AWS::EC2::KeyPair` | generic | +| ec2 | `aws_ec2_launch_template_versions` | `AWS::EC2::LaunchTemplateVersion` | generic | +| ec2 | `aws_ec2_launch_templates` | `AWS::EC2::LaunchTemplate` | generic | +| ec2 | `aws_ec2_managed_prefix_list_entries` | `AWS::EC2::ManagedPrefixListEntry` | generic | +| ec2 | `aws_ec2_managed_prefix_lists` | `AWS::EC2::ManagedPrefixList` | generic | +| ec2 | `aws_ec2_nat_gateways` | `AWS::EC2::NatGateway` | generic | +| ec2 | `aws_ec2_network_acls` | `AWS::EC2::NetworkAcl` | generic | +| ec2 | `aws_ec2_network_interfaces` | `AWS::EC2::NetworkInterface` | generic | +| ec2 | `aws_ec2_prefix_lists` | `AWS::EC2::PrefixList` | generic | +| ec2 | `aws_ec2_regional_configs` | `AWS::EC2::RegionalConfig` | generic | +| ec2 | `aws_ec2_replace_root_volume_tasks` | `AWS::EC2::ReplaceRootVolumeTask` | generic | +| ec2 | `aws_ec2_reserved_instances` | `AWS::EC2::ReservedInstance` | generic | +| ec2 | `aws_ec2_route_tables` | `AWS::EC2::RouteTable` | generic | +| ec2 | `aws_ec2_security_group_rules` | `AWS::EC2::SecurityGroupRule` | generic | +| ec2 | `aws_ec2_security_groups` | `AWS::EC2::SecurityGroup` | generic | +| ec2 | `aws_ec2_serial_console_access_statuses` | `AWS::EC2::SerialConsoleAccessStatus` | generic | +| ec2 | `aws_ec2_snapshot_block_public_access_states` | `AWS::EC2::SnapshotBlockPublicAccessState` | generic | +| ec2 | `aws_ec2_spot_fleet_instances` | `AWS::EC2::SpotFleetInstance` | generic | +| ec2 | `aws_ec2_spot_fleet_requests` | `AWS::EC2::SpotFleetRequest` | generic | +| ec2 | `aws_ec2_spot_instance_requests` | `AWS::EC2::SpotInstanceRequest` | generic | +| ec2 | `aws_ec2_subnets` | `AWS::EC2::Subnet` | generic | +| ec2 | `aws_ec2_traffic_mirror_filters` | `AWS::EC2::TrafficMirrorFilter` | generic | +| ec2 | `aws_ec2_traffic_mirror_sessions` | `AWS::EC2::TrafficMirrorSession` | generic | +| ec2 | `aws_ec2_traffic_mirror_targets` | `AWS::EC2::TrafficMirrorTarget` | generic | +| ec2 | `aws_ec2_transit_gateway_attachments` | `AWS::EC2::TransitGatewayAttachment` | generic | +| ec2 | `aws_ec2_transit_gateway_connect_peers` | `AWS::EC2::TransitGatewayConnectPeer` | generic | +| ec2 | `aws_ec2_transit_gateway_multicast_domains` | `AWS::EC2::TransitGatewayMulticastDomain` | generic | +| ec2 | `aws_ec2_transit_gateway_peering_attachments` | `AWS::EC2::TransitGatewayPeeringAttachment` | generic | +| ec2 | `aws_ec2_transit_gateway_route_tables` | `AWS::EC2::TransitGatewayRouteTable` | generic | +| ec2 | `aws_ec2_transit_gateway_routes` | `AWS::EC2::TransitGatewayRoute` | generic | +| ec2 | `aws_ec2_transit_gateway_vpc_attachments` | `AWS::EC2::TransitGatewayVpcAttachment` | generic | +| ec2 | `aws_ec2_transit_gateways` | `AWS::EC2::TransitGateway` | generic | +| ec2 | `aws_ec2_vpc_endpoint_connections` | `AWS::EC2::VpcEndpointConnection` | generic | +| ec2 | `aws_ec2_vpc_endpoint_service_configurations` | `AWS::EC2::VpcEndpointServiceConfiguration` | generic | +| ec2 | `aws_ec2_vpc_endpoint_service_permissions` | `AWS::EC2::VpcEndpointServicePermission` | generic | +| ec2 | `aws_ec2_vpc_endpoint_services` | `AWS::EC2::VpcEndpointService` | generic | +| ec2 | `aws_ec2_vpc_endpoints` | `AWS::EC2::VPCEndpoint` | generic | +| ec2 | `aws_ec2_vpc_peering_connections` | `AWS::EC2::VPCPeeringConnection` | generic | +| ec2 | `aws_ec2_vpcs` | `AWS::EC2::VPC` | generic | +| ec2 | `aws_ec2_vpn_connections` | `AWS::EC2::VPNConnection` | generic | +| ec2 | `aws_ec2_vpn_gateways` | `AWS::EC2::VPNGateway` | generic | +| ecr | `aws_ecr_pull_through_cache_rules` | `AWS::ECR::PullThroughCacheRule` | generic | +| ecr | `aws_ecr_registries` | `AWS::ECR::Registry` | generic | +| ecr | `aws_ecr_registry_policies` | `AWS::ECR::RegistryPolicy` | generic | +| ecr | `aws_ecr_repositories` | `AWS::ECR::Repository` | generic | +| ecr | `aws_ecr_repository_image_scan_findings` | `AWS::ECR::RepositoryImageScanFinding` | generic | +| ecr | `aws_ecr_repository_images` | `AWS::ECR::RepositoryImage` | generic | +| ecr | `aws_ecr_repository_lifecycle_policies` | `AWS::ECR::RepositoryLifecyclePolicy` | generic | +| ecr | `aws_ecr_repository_policies` | `AWS::ECR::RepositoryPolicy` | generic | +| ecrpublic | `aws_ecrpublic_repositories` | `AWS::Ecrpublic::Repository` | generic | +| ecrpublic | `aws_ecrpublic_repository_images` | `AWS::Ecrpublic::RepositoryImage` | generic | +| ecs | `aws_ecs_cluster_container_instances` | `AWS::ECS::ClusterContainerInstance` | generic | +| ecs | `aws_ecs_cluster_services` | `AWS::ECS::Service` | generic | +| ecs | `aws_ecs_cluster_task_sets` | `AWS::ECS::ClusterTaskSet` | generic | +| ecs | `aws_ecs_cluster_tasks` | `AWS::ECS::ClusterTask` | generic | +| ecs | `aws_ecs_clusters` | `AWS::ECS::Cluster` | generic | +| ecs | `aws_ecs_task_definitions` | `AWS::ECS::TaskDefinition` | generic | +| efs | `aws_efs_access_points` | `AWS::EFS::AccessPoint` | generic | +| efs | `aws_efs_filesystems` | `AWS::EFS::FileSystem` | generic | +| eks | `aws_eks_access_policies` | `AWS::EKS::AccessPolicy` | generic | +| eks | `aws_eks_cluster_access_entries` | `AWS::EKS::ClusterAccessEntry` | generic | +| eks | `aws_eks_cluster_addons` | `AWS::EKS::ClusterAddon` | generic | +| eks | `aws_eks_cluster_associated_access_policies` | `AWS::EKS::ClusterAssociatedAccessPolicy` | generic | +| eks | `aws_eks_cluster_node_groups` | `AWS::EKS::Nodegroup` | generic | +| eks | `aws_eks_cluster_oidc_identity_provider_configs` | `AWS::EKS::ClusterOidcIdentityProviderConfig` | generic | +| eks | `aws_eks_cluster_versions` | `AWS::EKS::ClusterVersion` | generic | +| eks | `aws_eks_clusters` | `AWS::EKS::Cluster` | generic | +| eks | `aws_eks_fargate_profiles` | `AWS::EKS::FargateProfile` | generic | +| elasticache | `aws_elasticache_clusters` | `AWS::ElastiCache::CacheCluster` | generic | +| elasticache | `aws_elasticache_engine_versions` | `AWS::ElastiCache::EngineVersion` | generic | +| elasticache | `aws_elasticache_events` | `AWS::ElastiCache::Event` | generic | +| elasticache | `aws_elasticache_global_replication_groups` | `AWS::ElastiCache::GlobalReplicationGroup` | generic | +| elasticache | `aws_elasticache_parameter_groups` | `AWS::ElastiCache::ParameterGroup` | generic | +| elasticache | `aws_elasticache_replication_groups` | `AWS::ElastiCache::ReplicationGroup` | generic | +| elasticache | `aws_elasticache_reserved_cache_nodes` | `AWS::ElastiCache::ReservedCacheNode` | generic | +| elasticache | `aws_elasticache_reserved_cache_nodes_offerings` | `AWS::ElastiCache::ReservedCacheNodesOffering` | generic | +| elasticache | `aws_elasticache_serverless_cache_snapshots` | `AWS::ElastiCache::ServerlessCacheSnapshot` | generic | +| elasticache | `aws_elasticache_serverless_caches` | `AWS::ElastiCache::ServerlessCach` | generic | +| elasticache | `aws_elasticache_service_updates` | `AWS::ElastiCache::ServiceUpdate` | generic | +| elasticache | `aws_elasticache_snapshots` | `AWS::ElastiCache::Snapshot` | generic | +| elasticache | `aws_elasticache_subnet_groups` | `AWS::ElastiCache::SubnetGroup` | generic | +| elasticache | `aws_elasticache_update_actions` | `AWS::ElastiCache::UpdateAction` | generic | +| elasticache | `aws_elasticache_user_groups` | `AWS::ElastiCache::UserGroup` | generic | +| elasticache | `aws_elasticache_users` | `AWS::ElastiCache::User` | generic | +| elasticbeanstalk | `aws_elasticbeanstalk_application_versions` | `AWS::ElasticBeanstalk::ApplicationVersion` | generic | +| elasticbeanstalk | `aws_elasticbeanstalk_applications` | `AWS::ElasticBeanstalk::Application` | generic | +| elasticbeanstalk | `aws_elasticbeanstalk_configuration_options` | `AWS::ElasticBeanstalk::ConfigurationOption` | generic | +| elasticbeanstalk | `aws_elasticbeanstalk_configuration_settings` | `AWS::ElasticBeanstalk::ConfigurationSetting` | generic | +| elasticbeanstalk | `aws_elasticbeanstalk_environments` | `AWS::ElasticBeanstalk::Environment` | generic | +| elasticbeanstalk | `aws_elasticbeanstalk_platform_versions` | `AWS::ElasticBeanstalk::PlatformVersion` | generic | +| elasticsearch | `aws_elasticsearch_domains` | `AWS::Elasticsearch::Domain` | generic | +| elasticsearch | `aws_elasticsearch_packages` | `AWS::Elasticsearch::Package` | generic | +| elasticsearch | `aws_elasticsearch_reserved_instances` | `AWS::Elasticsearch::ReservedInstance` | generic | +| elasticsearch | `aws_elasticsearch_versions` | `AWS::Elasticsearch::Version` | generic | +| elasticsearch | `aws_elasticsearch_vpc_endpoints` | `AWS::Elasticsearch::VpcEndpoint` | generic | +| elbv1 | `aws_elbv1_load_balancer_policies` | `AWS::ElasticLoadBalancing::LoadBalancerPolicy` | generic | +| elbv1 | `aws_elbv1_load_balancers` | `AWS::ElasticLoadBalancing::LoadBalancer` | generic | +| elbv2 | `aws_elbv2_listener_certificates` | `AWS::ElasticLoadBalancingV2::ListenerCertificate` | generic | +| elbv2 | `aws_elbv2_listener_rules` | `AWS::ElasticLoadBalancingV2::ListenerRule` | generic | +| elbv2 | `aws_elbv2_listeners` | `AWS::ElasticLoadBalancingV2::Listener` | generic | +| elbv2 | `aws_elbv2_load_balancer_attributes` | `AWS::ElasticLoadBalancingV2::LoadBalancerAttribute` | generic | +| elbv2 | `aws_elbv2_load_balancer_capacity_reservations` | `AWS::ElasticLoadBalancingV2::LoadBalancerCapacityReservation` | generic | +| elbv2 | `aws_elbv2_load_balancer_web_acls` | `AWS::ElasticLoadBalancingV2::LoadBalancerWebAcl` | generic | +| elbv2 | `aws_elbv2_load_balancers` | `AWS::ElasticLoadBalancingV2::LoadBalancer` | generic | +| elbv2 | `aws_elbv2_target_group_attributes` | `AWS::ElasticLoadBalancingV2::TargetGroupAttribute` | generic | +| elbv2 | `aws_elbv2_target_group_target_health_descriptions` | `AWS::ElasticLoadBalancingV2::TargetGroupTargetHealthDescription` | generic | +| elbv2 | `aws_elbv2_target_groups` | `AWS::ElasticLoadBalancingV2::TargetGroup` | generic | +| emr | `aws_emr_block_public_access_configs` | `AWS::EMR::BlockPublicAccessConfig` | generic | +| emr | `aws_emr_cluster_instance_fleets` | `AWS::EMR::ClusterInstanceFleet` | generic | +| emr | `aws_emr_cluster_instance_groups` | `AWS::EMR::ClusterInstanceGroup` | generic | +| emr | `aws_emr_cluster_instances` | `AWS::EMR::ClusterInstance` | generic | +| emr | `aws_emr_clusters` | `AWS::EMR::Cluster` | generic | +| emr | `aws_emr_notebook_executions` | `AWS::EMR::NotebookExecution` | generic | +| emr | `aws_emr_release_labels` | `AWS::EMR::ReleaseLabel` | generic | +| emr | `aws_emr_security_configurations` | `AWS::EMR::SecurityConfiguration` | generic | +| emr | `aws_emr_steps` | `AWS::EMR::Step` | generic | +| emr | `aws_emr_studio_session_mappings` | `AWS::EMR::StudioSessionMapping` | generic | +| emr | `aws_emr_studios` | `AWS::EMR::Studio` | generic | +| emr | `aws_emr_supported_instance_types` | `AWS::EMR::SupportedInstanceType` | generic | +| eventbridge | `aws_eventbridge_api_destinations` | `AWS::Events::ApiDestination` | generic | +| eventbridge | `aws_eventbridge_archives` | `AWS::Events::Archive` | generic | +| eventbridge | `aws_eventbridge_connections` | `AWS::Events::Connection` | generic | +| eventbridge | `aws_eventbridge_endpoints` | `AWS::Events::Endpoint` | generic | +| eventbridge | `aws_eventbridge_event_bus_rules` | `AWS::Events::EventBusRule` | generic | +| eventbridge | `aws_eventbridge_event_bus_targets` | `AWS::Events::EventBusTarget` | generic | +| eventbridge | `aws_eventbridge_event_buses` | `AWS::Events::EventBus` | generic | +| eventbridge | `aws_eventbridge_event_sources` | `AWS::Events::EventSource` | generic | +| eventbridge | `aws_eventbridge_replays` | `AWS::Events::Replay` | generic | +| firehose | `aws_firehose_delivery_streams` | `AWS::KinesisFirehose::DeliveryStream` | generic | +| fis | `aws_fis_actions` | `AWS::Fis::Action` | generic | +| fis | `aws_fis_experiment_resolved_targets` | `AWS::Fis::ExperimentResolvedTarget` | generic | +| fis | `aws_fis_experiment_templates` | `AWS::Fis::ExperimentTemplate` | generic | +| fis | `aws_fis_experiments` | `AWS::Fis::Experiment` | generic | +| fis | `aws_fis_target_account_configurations` | `AWS::Fis::TargetAccountConfiguration` | generic | +| fis | `aws_fis_target_resource_types` | `AWS::Fis::TargetResourceType` | generic | +| frauddetector | `aws_frauddetector_batch_imports` | `AWS::FraudDetector::BatchImport` | generic | +| frauddetector | `aws_frauddetector_batch_predictions` | `AWS::FraudDetector::BatchPrediction` | generic | +| frauddetector | `aws_frauddetector_detectors` | `AWS::FraudDetector::Detector` | generic | +| frauddetector | `aws_frauddetector_entity_types` | `AWS::FraudDetector::EntityType` | generic | +| frauddetector | `aws_frauddetector_event_types` | `AWS::FraudDetector::EventType` | generic | +| frauddetector | `aws_frauddetector_external_models` | `AWS::FraudDetector::ExternalModel` | generic | +| frauddetector | `aws_frauddetector_labels` | `AWS::FraudDetector::Label` | generic | +| frauddetector | `aws_frauddetector_model_versions` | `AWS::FraudDetector::ModelVersion` | generic | +| frauddetector | `aws_frauddetector_models` | `AWS::FraudDetector::Model` | generic | +| frauddetector | `aws_frauddetector_outcomes` | `AWS::FraudDetector::Outcome` | generic | +| frauddetector | `aws_frauddetector_rules` | `AWS::FraudDetector::Rule` | generic | +| frauddetector | `aws_frauddetector_variables` | `AWS::FraudDetector::Variable` | generic | +| freetier | `aws_freetier_usages` | `AWS::Freetier::Usage` | generic | +| fsx | `aws_fsx_backups` | `AWS::FSx::Backup` | generic | +| fsx | `aws_fsx_data_repository_associations` | `AWS::FSx::DataRepositoryAssociation` | generic | +| fsx | `aws_fsx_data_repository_tasks` | `AWS::FSx::DataRepositoryTask` | generic | +| fsx | `aws_fsx_file_caches` | `AWS::FSx::FileCach` | generic | +| fsx | `aws_fsx_file_systems` | `AWS::FSx::FileSystem` | generic | +| fsx | `aws_fsx_snapshots` | `AWS::FSx::Snapshot` | generic | +| fsx | `aws_fsx_storage_virtual_machines` | `AWS::FSx::StorageVirtualMachine` | generic | +| fsx | `aws_fsx_volumes` | `AWS::FSx::Volume` | generic | +| glacier | `aws_glacier_data_retrieval_policies` | `AWS::Glacier::DataRetrievalPolicy` | generic | +| glacier | `aws_glacier_vault_access_policies` | `AWS::Glacier::VaultAccessPolicy` | generic | +| glacier | `aws_glacier_vault_lock_policies` | `AWS::Glacier::VaultLockPolicy` | generic | +| glacier | `aws_glacier_vault_notifications` | `AWS::Glacier::VaultNotification` | generic | +| glacier | `aws_glacier_vaults` | `AWS::Glacier::Vault` | generic | +| globalaccelerator | `aws_globalaccelerator_accelerators` | `AWS::GlobalAccelerator::Accelerator` | generic | +| globalaccelerator | `aws_globalaccelerator_custom_routing_accelerators` | `AWS::GlobalAccelerator::CustomRoutingAccelerator` | generic | +| globalaccelerator | `aws_globalaccelerator_endpoint_groups` | `AWS::GlobalAccelerator::EndpointGroup` | generic | +| globalaccelerator | `aws_globalaccelerator_listeners` | `AWS::GlobalAccelerator::Listener` | generic | +| glue | `aws_glue_catalogs` | `AWS::Glue::Catalog` | generic | +| glue | `aws_glue_classifiers` | `AWS::Glue::Classifier` | generic | +| glue | `aws_glue_connections` | `AWS::Glue::Connection` | generic | +| glue | `aws_glue_crawlers` | `AWS::Glue::Crawler` | generic | +| glue | `aws_glue_database_table_indexes` | `AWS::Glue::DatabaseTableIndex` | generic | +| glue | `aws_glue_database_table_storage_optimizers` | `AWS::Glue::DatabaseTableStorageOptimizer` | generic | +| glue | `aws_glue_database_tables` | `AWS::Glue::DatabaseTable` | generic | +| glue | `aws_glue_databases` | `AWS::Glue::Databas` | generic | +| glue | `aws_glue_datacatalog_encryption_settings` | `AWS::Glue::DatacatalogEncryptionSetting` | generic | +| glue | `aws_glue_dev_endpoints` | `AWS::Glue::DevEndpoint` | generic | +| glue | `aws_glue_job_runs` | `AWS::Glue::JobRun` | generic | +| glue | `aws_glue_jobs` | `AWS::Glue::Job` | generic | +| glue | `aws_glue_ml_transform_task_runs` | `AWS::Glue::MlTransformTaskRun` | generic | +| glue | `aws_glue_ml_transforms` | `AWS::Glue::MlTransform` | generic | +| glue | `aws_glue_registries` | `AWS::Glue::Registry` | generic | +| glue | `aws_glue_registry_schema_versions` | `AWS::Glue::RegistrySchemaVersion` | generic | +| glue | `aws_glue_registry_schemas` | `AWS::Glue::RegistrySchema` | generic | +| glue | `aws_glue_security_configurations` | `AWS::Glue::SecurityConfiguration` | generic | +| glue | `aws_glue_triggers` | `AWS::Glue::Trigger` | generic | +| glue | `aws_glue_workflows` | `AWS::Glue::Workflow` | generic | +| grafana | `aws_grafana_permissions` | `AWS::Grafana::Permission` | generic | +| grafana | `aws_grafana_versions` | `AWS::Grafana::Version` | generic | +| grafana | `aws_grafana_workspace_service_account_tokens` | `AWS::Grafana::WorkspaceServiceAccountToken` | generic | +| grafana | `aws_grafana_workspace_service_accounts` | `AWS::Grafana::WorkspaceServiceAccount` | generic | +| grafana | `aws_grafana_workspaces` | `AWS::Grafana::Workspace` | generic | +| guardduty | `aws_guardduty_detector_coverages` | `AWS::GuardDuty::DetectorCoverage` | generic | +| guardduty | `aws_guardduty_detector_filters` | `AWS::GuardDuty::DetectorFilter` | generic | +| guardduty | `aws_guardduty_detector_findings` | `AWS::GuardDuty::DetectorFinding` | generic | +| guardduty | `aws_guardduty_detector_intel_sets` | `AWS::GuardDuty::DetectorIntelSet` | generic | +| guardduty | `aws_guardduty_detector_ip_sets` | `AWS::GuardDuty::DetectorIpSet` | generic | +| guardduty | `aws_guardduty_detector_members` | `AWS::GuardDuty::DetectorMember` | generic | +| guardduty | `aws_guardduty_detector_publishing_destinations` | `AWS::GuardDuty::DetectorPublishingDestination` | generic | +| guardduty | `aws_guardduty_detectors` | `AWS::GuardDuty::Detector` | generic | +| health | `aws_health_affected_entities` | `AWS::Health::AffectedEntity` | generic | +| health | `aws_health_event_details` | `AWS::Health::EventDetail` | generic | +| health | `aws_health_events` | `AWS::Health::Event` | generic | +| health | `aws_health_org_event_details` | `AWS::Health::OrgEventDetail` | generic | +| health | `aws_health_organization_affected_entities` | `AWS::Health::OrganizationAffectedEntity` | generic | +| health | `aws_health_organization_events` | `AWS::Health::OrganizationEvent` | generic | +| healthlake | `aws_healthlake_fhir_datastores` | `AWS::HealthLake::FhirDatastore` | generic | +| iam | `aws_iam_account_authorization_details` | `-` | generic | +| iam | `aws_iam_accounts` | `-` | typed | +| iam | `aws_iam_credential_reports` | `-` | generic | +| iam | `aws_iam_group_attached_policies` | `AWS::IAM::GroupAttachedPolicy` | generic | +| iam | `aws_iam_group_last_accessed_details` | `AWS::IAM::GroupLastAccessedDetail` | generic | +| iam | `aws_iam_group_policies` | `AWS::IAM::GroupPolicy` | generic | +| iam | `aws_iam_groups` | `AWS::IAM::Group` | generic | +| iam | `aws_iam_instance_profiles` | `AWS::IAM::InstanceProfile` | generic | +| iam | `aws_iam_mfa_devices` | `AWS::IAM::MfaDevice` | generic | +| iam | `aws_iam_openid_connect_identity_providers` | `AWS::IAM::OpenidConnectIdentityProvider` | generic | +| iam | `aws_iam_outbound_web_identity_federations` | `AWS::IAM::OutboundWebIdentityFederation` | generic | +| iam | `aws_iam_password_policies` | `AWS::IAM::PasswordPolicy` | generic | +| iam | `aws_iam_policies` | `AWS::IAM::ManagedPolicy` | generic | +| iam | `aws_iam_policy_default_versions` | `AWS::IAM::PolicyDefaultVersion` | generic | +| iam | `aws_iam_policy_last_accessed_details` | `AWS::IAM::PolicyLastAccessedDetail` | generic | +| iam | `aws_iam_policy_versions` | `AWS::IAM::PolicyVersion` | generic | +| iam | `aws_iam_role_attached_policies` | `AWS::IAM::RoleAttachedPolicy` | generic | +| iam | `aws_iam_role_last_accessed_details` | `AWS::IAM::RoleLastAccessedDetail` | generic | +| iam | `aws_iam_role_policies` | `AWS::IAM::RolePolicy` | generic | +| iam | `aws_iam_roles` | `AWS::IAM::Role` | generic | +| iam | `aws_iam_saml_identity_providers` | `AWS::IAM::SamlIdentityProvider` | generic | +| iam | `aws_iam_server_certificates` | `AWS::IAM::ServerCertificate` | generic | +| iam | `aws_iam_signing_certificates` | `AWS::IAM::SigningCertificate` | generic | +| iam | `aws_iam_ssh_public_keys` | `AWS::IAM::SshPublicKey` | generic | +| iam | `aws_iam_user_access_keys` | `AWS::IAM::UserAccessKey` | generic | +| iam | `aws_iam_user_attached_policies` | `AWS::IAM::UserAttachedPolicy` | generic | +| iam | `aws_iam_user_groups` | `AWS::IAM::UserGroup` | generic | +| iam | `aws_iam_user_last_accessed_details` | `AWS::IAM::UserLastAccessedDetail` | generic | +| iam | `aws_iam_user_policies` | `AWS::IAM::UserPolicy` | generic | +| iam | `aws_iam_users` | `AWS::IAM::User` | generic | +| iam | `aws_iam_virtual_mfa_devices` | `AWS::IAM::VirtualMfaDevice` | generic | +| identitystore | `aws_identitystore_group_memberships` | `AWS::IdentityStore::GroupMembership` | generic | +| identitystore | `aws_identitystore_groups` | `AWS::IdentityStore::Group` | generic | +| identitystore | `aws_identitystore_users` | `AWS::IdentityStore::User` | generic | +| imagebuilder | `aws_imagebuilder_distribution_configurations` | `AWS::ImageBuilder::DistributionConfiguration` | generic | +| imagebuilder | `aws_imagebuilder_images` | `AWS::ImageBuilder::Image` | generic | +| imagebuilder | `aws_imagebuilder_workflows` | `AWS::ImageBuilder::Workflow` | generic | +| inspector | `aws_inspector_findings` | `AWS::Inspector::Finding` | generic | +| inspector2 | `aws_inspector2_cis_scan_result_details` | `AWS::InspectorV2::CisScanResultDetail` | generic | +| inspector2 | `aws_inspector2_cis_scans` | `AWS::InspectorV2::CisScan` | generic | +| inspector2 | `aws_inspector2_cis_target_resource_aggregations` | `AWS::InspectorV2::CisTargetResourceAggregation` | generic | +| inspector2 | `aws_inspector2_covered_resources` | `AWS::InspectorV2::CoveredResource` | generic | +| inspector2 | `aws_inspector2_findings` | `AWS::InspectorV2::Finding` | generic | +| invoicing | `aws_invoicing_invoice_units` | `AWS::Invoicing::InvoiceUnit` | generic | +| iot | `aws_iot_billing_groups` | `AWS::IoT::BillingGroup` | generic | +| iot | `aws_iot_ca_certificates` | `AWS::IoT::CaCertificate` | generic | +| iot | `aws_iot_certificates` | `AWS::IoT::Certificate` | generic | +| iot | `aws_iot_jobs` | `AWS::IoT::Job` | generic | +| iot | `aws_iot_policies` | `AWS::IoT::Policy` | generic | +| iot | `aws_iot_security_profiles` | `AWS::IoT::SecurityProfile` | generic | +| iot | `aws_iot_streams` | `AWS::IoT::Stream` | generic | +| iot | `aws_iot_thing_groups` | `AWS::IoT::ThingGroup` | generic | +| iot | `aws_iot_thing_types` | `AWS::IoT::ThingType` | generic | +| iot | `aws_iot_things` | `AWS::IoT::Thing` | generic | +| iot | `aws_iot_topic_rules` | `AWS::IoT::TopicRule` | generic | +| kafka | `aws_kafka_cluster_operations` | `AWS::MSK::ClusterOperation` | generic | +| kafka | `aws_kafka_cluster_policies` | `AWS::MSK::ClusterPolicy` | generic | +| kafka | `aws_kafka_clusters` | `AWS::MSK::Cluster` | generic | +| kafka | `aws_kafka_configurations` | `AWS::MSK::Configuration` | generic | +| kafka | `aws_kafka_nodes` | `AWS::MSK::Node` | generic | +| kendra | `aws_kendra_access_control_configurations` | `AWS::Kendra::AccessControlConfiguration` | generic | +| kendra | `aws_kendra_data_source_sync_jobs` | `AWS::Kendra::DataSourceSyncJob` | generic | +| kendra | `aws_kendra_data_sources` | `AWS::Kendra::DataSource` | generic | +| kendra | `aws_kendra_experience_entities` | `AWS::Kendra::ExperienceEntity` | generic | +| kendra | `aws_kendra_experience_entity_personas` | `AWS::Kendra::ExperienceEntityPersona` | generic | +| kendra | `aws_kendra_experiences` | `AWS::Kendra::Experience` | generic | +| kendra | `aws_kendra_faqs` | `AWS::Kendra::Faq` | generic | +| kendra | `aws_kendra_featured_results_sets` | `AWS::Kendra::FeaturedResultsSet` | generic | +| kendra | `aws_kendra_indices` | `AWS::Kendra::Index` | generic | +| kendra | `aws_kendra_query_suggestions_block_lists` | `AWS::Kendra::QuerySuggestionsBlockList` | generic | +| kendra | `aws_kendra_query_suggestions_configs` | `AWS::Kendra::QuerySuggestionsConfig` | generic | +| kendra | `aws_kendra_thesauri` | `AWS::Kendra::Thesaurus` | generic | +| keyspaces | `aws_keyspaces_keyspaces` | `AWS::Cassandra::Keyspace` | generic | +| keyspaces | `aws_keyspaces_tables` | `AWS::Cassandra::Table` | generic | +| kinesis | `aws_kinesis_stream_consumers` | `AWS::Kinesis::StreamConsumer` | generic | +| kinesis | `aws_kinesis_stream_shards` | `AWS::Kinesis::StreamShard` | generic | +| kinesis | `aws_kinesis_streams` | `AWS::Kinesis::Stream` | generic | +| kinesisanalytics | `aws_kinesisanalytics_application_operations` | `AWS::KinesisAnalyticsV2::ApplicationOperation` | generic | +| kinesisanalytics | `aws_kinesisanalytics_application_snapshots` | `AWS::KinesisAnalyticsV2::ApplicationSnapshot` | generic | +| kinesisanalytics | `aws_kinesisanalytics_application_versions` | `AWS::KinesisAnalyticsV2::ApplicationVersion` | generic | +| kinesisanalytics | `aws_kinesisanalytics_applications` | `AWS::KinesisAnalyticsV2::Application` | generic | +| kinesisvideo | `aws_kinesisvideo_streams` | `AWS::KinesisVideo::Stream` | generic | +| kms | `aws_kms_aliases` | `AWS::KMS::Alias` | generic | +| kms | `aws_kms_key_grants` | `AWS::KMS::KeyGrant` | generic | +| kms | `aws_kms_key_policies` | `AWS::KMS::KeyPolicy` | generic | +| kms | `aws_kms_key_rotation_statuses` | `AWS::KMS::KeyRotationStatus` | generic | +| kms | `aws_kms_key_rotations` | `AWS::KMS::KeyRotation` | generic | +| kms | `aws_kms_keys` | `AWS::KMS::Key` | generic | +| lakeformation | `aws_lakeformation_data_cells_filters` | `AWS::LakeFormation::DataCellsFilter` | generic | +| lakeformation | `aws_lakeformation_opt_ins` | `AWS::LakeFormation::OptIn` | generic | +| lakeformation | `aws_lakeformation_permissions` | `AWS::LakeFormation::Permission` | generic | +| lakeformation | `aws_lakeformation_resource_tags` | `AWS::LakeFormation::ResourceTag` | generic | +| lakeformation | `aws_lakeformation_resources` | `AWS::LakeFormation::Resource` | generic | +| lakeformation | `aws_lakeformation_tags` | `AWS::LakeFormation::Tag` | generic | +| lakeformation | `aws_lakeformation_transactions` | `AWS::LakeFormation::Transaction` | generic | +| lambda | `aws_lambda_function_aliases` | `AWS::Lambda::FunctionAlias` | generic | +| lambda | `aws_lambda_function_concurrency_configs` | `AWS::Lambda::FunctionConcurrencyConfig` | generic | +| lambda | `aws_lambda_function_event_invoke_configs` | `AWS::Lambda::FunctionEventInvokeConfig` | generic | +| lambda | `aws_lambda_function_event_source_mappings` | `AWS::Lambda::FunctionEventSourceMapping` | generic | +| lambda | `aws_lambda_function_url_configs` | `AWS::Lambda::FunctionUrlConfig` | generic | +| lambda | `aws_lambda_function_versions` | `AWS::Lambda::FunctionVersion` | generic | +| lambda | `aws_lambda_functions` | `AWS::Lambda::Function` | generic | +| lambda | `aws_lambda_layer_version_policies` | `AWS::Lambda::LayerVersionPolicy` | generic | +| lambda | `aws_lambda_layer_versions` | `AWS::Lambda::LayerVersion` | generic | +| lambda | `aws_lambda_layers` | `AWS::Lambda::LayerVersion` | generic | +| lambda | `aws_lambda_runtimes` | `AWS::Lambda::Runtime` | generic | +| lex | `aws_lex_bot_aliases` | `AWS::Lex::BotAlias` | generic | +| lex | `aws_lex_bot_channel_associations` | `AWS::Lex::BotChannelAssociation` | generic | +| lex | `aws_lex_bot_version_utterances_views` | `AWS::Lex::BotVersionUtterancesView` | generic | +| lex | `aws_lex_bot_versions` | `AWS::Lex::BotVersion` | generic | +| lex | `aws_lex_bots` | `AWS::Lex::Bot` | generic | +| lex | `aws_lex_builtin_intents` | `AWS::Lex::BuiltinIntent` | generic | +| lex | `aws_lex_builtin_slot_types` | `AWS::Lex::BuiltinSlotType` | generic | +| lex | `aws_lex_intent_versions` | `AWS::Lex::IntentVersion` | generic | +| lex | `aws_lex_intents` | `AWS::Lex::Intent` | generic | +| lex | `aws_lex_migrations` | `AWS::Lex::Migration` | generic | +| lex | `aws_lex_slot_type_versions` | `AWS::Lex::SlotTypeVersion` | generic | +| lex | `aws_lex_slot_types` | `AWS::Lex::SlotType` | generic | +| lexv2 | `aws_lexv2_bot_aliases` | `AWS::LexV2::BotAlias` | generic | +| lexv2 | `aws_lexv2_bots` | `AWS::LexV2::Bot` | generic | +| lightsail | `aws_lightsail_alarms` | `AWS::Lightsail::Alarm` | generic | +| lightsail | `aws_lightsail_bucket_access_keys` | `AWS::Lightsail::BucketAccessKey` | generic | +| lightsail | `aws_lightsail_buckets` | `AWS::Lightsail::Bucket` | generic | +| lightsail | `aws_lightsail_certificates` | `AWS::Lightsail::Certificate` | generic | +| lightsail | `aws_lightsail_container_service_deployments` | `AWS::Lightsail::ContainerServiceDeployment` | generic | +| lightsail | `aws_lightsail_container_service_images` | `AWS::Lightsail::ContainerServiceImage` | generic | +| lightsail | `aws_lightsail_container_services` | `AWS::Lightsail::ContainerService` | generic | +| lightsail | `aws_lightsail_database_events` | `AWS::Lightsail::DatabaseEvent` | generic | +| lightsail | `aws_lightsail_database_log_events` | `AWS::Lightsail::DatabaseLogEvent` | generic | +| lightsail | `aws_lightsail_database_parameters` | `AWS::Lightsail::DatabaseParameter` | generic | +| lightsail | `aws_lightsail_database_snapshots` | `AWS::Lightsail::DatabaseSnapshot` | generic | +| lightsail | `aws_lightsail_databases` | `AWS::Lightsail::Databas` | generic | +| lightsail | `aws_lightsail_disk_snapshots` | `AWS::Lightsail::DiskSnapshot` | generic | +| lightsail | `aws_lightsail_disks` | `AWS::Lightsail::Disk` | generic | +| lightsail | `aws_lightsail_distributions` | `AWS::Lightsail::Distribution` | generic | +| lightsail | `aws_lightsail_instance_port_states` | `AWS::Lightsail::InstancePortState` | generic | +| lightsail | `aws_lightsail_instance_snapshots` | `AWS::Lightsail::InstanceSnapshot` | generic | +| lightsail | `aws_lightsail_instances` | `AWS::Lightsail::Instance` | generic | +| lightsail | `aws_lightsail_load_balancer_tls_certificates` | `AWS::Lightsail::LoadBalancerTlsCertificate` | generic | +| lightsail | `aws_lightsail_load_balancers` | `AWS::Lightsail::LoadBalancer` | generic | +| lightsail | `aws_lightsail_static_ips` | `AWS::Lightsail::StaticIp` | generic | +| location | `aws_location_keys` | `AWS::Location::Key` | generic | +| location | `aws_location_maps` | `AWS::Location::Map` | generic | +| macie2 | `aws_macie2_allow_lists` | `AWS::Macie::AllowList` | generic | +| macie2 | `aws_macie2_automated_discovery_accounts` | `AWS::Macie::AutomatedDiscoveryAccount` | generic | +| macie2 | `aws_macie2_classification_jobs` | `AWS::Macie::ClassificationJob` | generic | +| macie2 | `aws_macie2_classification_scopes` | `AWS::Macie::ClassificationScope` | generic | +| macie2 | `aws_macie2_custom_data_identifiers` | `AWS::Macie::CustomDataIdentifier` | generic | +| macie2 | `aws_macie2_findings` | `AWS::Macie::Finding` | generic | +| macie2 | `aws_macie2_invitations` | `AWS::Macie::Invitation` | generic | +| macie2 | `aws_macie2_managed_data_identifiers` | `AWS::Macie::ManagedDataIdentifier` | generic | +| macie2 | `aws_macie2_members` | `AWS::Macie::Member` | generic | +| macie2 | `aws_macie2_sensitivity_inspection_templates` | `AWS::Macie::SensitivityInspectionTemplate` | generic | +| macie2 | `aws_macie2_usage_totals` | `AWS::Macie::UsageTotal` | generic | +| memorydb | `aws_memorydb_reserved_nodes` | `AWS::MemoryDB::Cluster` | generic | +| mpa | `aws_mpa_teams` | `AWS::Mpa::Team` | generic | +| mq | `aws_mq_broker_configuration_revisions` | `AWS::AmazonMQ::BrokerConfigurationRevision` | generic | +| mq | `aws_mq_broker_configurations` | `AWS::AmazonMQ::BrokerConfiguration` | generic | +| mq | `aws_mq_broker_users` | `AWS::AmazonMQ::BrokerUser` | generic | +| mq | `aws_mq_brokers` | `AWS::AmazonMQ::Broker` | generic | +| mwaa | `aws_mwaa_environments` | `AWS::MWAA::Environment` | generic | +| neptune | `aws_neptune_cluster_parameter_group_parameters` | `AWS::Neptune::ClusterParameterGroupParameter` | generic | +| neptune | `aws_neptune_cluster_parameter_groups` | `AWS::Neptune::ClusterParameterGroup` | generic | +| neptune | `aws_neptune_cluster_snapshots` | `AWS::Neptune::ClusterSnapshot` | generic | +| neptune | `aws_neptune_clusters` | `AWS::Neptune::DBCluster` | generic | +| neptune | `aws_neptune_db_parameter_group_db_parameters` | `AWS::Neptune::DbParameterGroupDbParameter` | generic | +| neptune | `aws_neptune_db_parameter_groups` | `AWS::Neptune::DbParameterGroup` | generic | +| neptune | `aws_neptune_event_subscriptions` | `AWS::Neptune::EventSubscription` | generic | +| neptune | `aws_neptune_global_clusters` | `AWS::Neptune::GlobalCluster` | generic | +| neptune | `aws_neptune_instances` | `AWS::Neptune::DBInstance` | generic | +| neptune | `aws_neptune_subnet_groups` | `AWS::Neptune::SubnetGroup` | generic | +| networkfirewall | `aws_networkfirewall_firewall_policies` | `AWS::NetworkFirewall::FirewallPolicy` | generic | +| networkfirewall | `aws_networkfirewall_firewalls` | `AWS::NetworkFirewall::Firewall` | generic | +| networkfirewall | `aws_networkfirewall_rule_groups` | `AWS::NetworkFirewall::RuleGroup` | generic | +| networkfirewall | `aws_networkfirewall_tls_inspection_configurations` | `AWS::NetworkFirewall::TlsInspectionConfiguration` | generic | +| networkmanager | `aws_networkmanager_attachments` | `AWS::NetworkManager::Attachment` | generic | +| networkmanager | `aws_networkmanager_core_network_policy_versions` | `AWS::NetworkManager::CoreNetworkPolicyVersion` | generic | +| networkmanager | `aws_networkmanager_core_networks` | `AWS::NetworkManager::CoreNetwork` | generic | +| networkmanager | `aws_networkmanager_global_networks` | `AWS::NetworkManager::GlobalNetwork` | generic | +| networkmanager | `aws_networkmanager_links` | `AWS::NetworkManager::Link` | generic | +| networkmanager | `aws_networkmanager_sites` | `AWS::NetworkManager::Site` | generic | +| networkmanager | `aws_networkmanager_transit_gateway_registrations` | `AWS::NetworkManager::TransitGatewayRegistration` | generic | +| odb | `aws_odb_autonomous_virtual_machines` | `AWS::Odb::AutonomousVirtualMachine` | generic | +| odb | `aws_odb_cloud_autonomous_vm_clusters` | `AWS::Odb::CloudAutonomousVmCluster` | generic | +| odb | `aws_odb_cloud_exadata_infrastructures` | `AWS::Odb::CloudExadataInfrastructure` | generic | +| odb | `aws_odb_cloud_vm_clusters` | `AWS::Odb::CloudVmCluster` | generic | +| odb | `aws_odb_db_nodes` | `AWS::Odb::DbNode` | generic | +| odb | `aws_odb_db_servers` | `AWS::Odb::DbServer` | generic | +| odb | `aws_odb_db_system_shapes` | `AWS::Odb::DbSystemShape` | generic | +| odb | `aws_odb_gi_versions` | `AWS::Odb::GiVersion` | generic | +| odb | `aws_odb_networks` | `AWS::Odb::Network` | generic | +| odb | `aws_odb_peering_connections` | `AWS::Odb::PeeringConnection` | generic | +| odb | `aws_odb_system_versions` | `AWS::Odb::SystemVersion` | generic | +| opensearch | `aws_opensearch_domain_auto_tunes` | `AWS::OpenSearchService::DomainAutoTune` | generic | +| opensearch | `aws_opensearch_domain_configs` | `AWS::OpenSearchService::DomainConfig` | generic | +| opensearch | `aws_opensearch_domain_data_sources` | `AWS::OpenSearchService::DomainDataSource` | generic | +| opensearch | `aws_opensearch_domain_health` | `AWS::OpenSearchService::DomainHealth` | generic | +| opensearch | `aws_opensearch_domain_maintenances` | `AWS::OpenSearchService::DomainMaintenance` | generic | +| opensearch | `aws_opensearch_domain_nodes` | `AWS::OpenSearchService::DomainNode` | generic | +| opensearch | `aws_opensearch_domain_packages` | `AWS::OpenSearchService::DomainPackage` | generic | +| opensearch | `aws_opensearch_domain_scheduled_actions` | `AWS::OpenSearchService::DomainScheduledAction` | generic | +| opensearch | `aws_opensearch_domains` | `AWS::OpenSearchService::Domain` | generic | +| opensearch | `aws_opensearch_inbound_connections` | `AWS::OpenSearchService::InboundConnection` | generic | +| opensearch | `aws_opensearch_outbound_connections` | `AWS::OpenSearchService::OutboundConnection` | generic | +| opensearch | `aws_opensearch_reserved_instances` | `AWS::OpenSearchService::ReservedInstance` | generic | +| opensearch | `aws_opensearch_versions` | `AWS::OpenSearchService::Version` | generic | +| opensearch | `aws_opensearch_vpc_endpoints` | `AWS::OpenSearchService::VpcEndpoint` | generic | +| organization | `aws_organization_resource_policies` | `AWS::Organization::ResourcePolicy` | generic | +| organizations | `aws_organizations` | `AWS::Organizations::Organization` | generic | +| organizations | `aws_organizations_account_parents` | `AWS::Organizations::AccountParent` | generic | +| organizations | `aws_organizations_accounts` | `AWS::Organizations::Account` | generic | +| organizations | `aws_organizations_delegated_administrators` | `AWS::Organizations::DelegatedAdministrator` | generic | +| organizations | `aws_organizations_delegated_services` | `AWS::Organizations::DelegatedService` | generic | +| organizations | `aws_organizations_organizational_unit_parents` | `AWS::Organizations::OrganizationalUnitParent` | generic | +| organizations | `aws_organizations_organizational_units` | `AWS::Organizations::OrganizationalUnit` | generic | +| organizations | `aws_organizations_policies` | `AWS::Organizations::Policy` | generic | +| organizations | `aws_organizations_policy_targets` | `AWS::Organizations::PolicyTarget` | generic | +| organizations | `aws_organizations_roots` | `AWS::Organizations::Root` | generic | +| pinpoint | `aws_pinpoint_apps` | `AWS::Pinpoint::App` | generic | +| pinpoint | `aws_pinpoint_campaign_versions` | `AWS::Pinpoint::CampaignVersion` | generic | +| pinpoint | `aws_pinpoint_campaigns` | `AWS::Pinpoint::Campaign` | generic | +| pinpoint | `aws_pinpoint_export_jobs` | `AWS::Pinpoint::ExportJob` | generic | +| pinpoint | `aws_pinpoint_import_jobs` | `AWS::Pinpoint::ImportJob` | generic | +| pinpoint | `aws_pinpoint_recommender_configurations` | `AWS::Pinpoint::RecommenderConfiguration` | generic | +| pinpoint | `aws_pinpoint_segments` | `AWS::Pinpoint::Segment` | generic | +| pinpoint | `aws_pinpoint_template_versions` | `AWS::Pinpoint::TemplateVersion` | generic | +| pinpoint | `aws_pinpoint_templates` | `AWS::Pinpoint::Template` | generic | +| polly | `aws_polly_lexicons` | `AWS::Polly::Lexicon` | generic | +| polly | `aws_polly_speech_synthesis_tasks` | `AWS::Polly::SpeechSynthesisTask` | generic | +| polly | `aws_polly_voices` | `AWS::Polly::Voice` | generic | +| quicksight | `aws_quicksight_analyses` | `AWS::QuickSight::Analysis` | generic | +| quicksight | `aws_quicksight_dashboards` | `AWS::QuickSight::Dashboard` | generic | +| quicksight | `aws_quicksight_data_sets` | `AWS::QuickSight::DataSet` | generic | +| quicksight | `aws_quicksight_data_sources` | `AWS::QuickSight::DataSource` | generic | +| quicksight | `aws_quicksight_folders` | `AWS::QuickSight::Folder` | generic | +| quicksight | `aws_quicksight_group_members` | `AWS::QuickSight::GroupMember` | generic | +| quicksight | `aws_quicksight_groups` | `AWS::QuickSight::Group` | generic | +| quicksight | `aws_quicksight_ingestions` | `AWS::QuickSight::Ingestion` | generic | +| quicksight | `aws_quicksight_templates` | `AWS::QuickSight::Template` | generic | +| quicksight | `aws_quicksight_users` | `AWS::QuickSight::User` | generic | +| ram | `aws_ram_principals` | `AWS::Ram::Principal` | generic | +| ram | `aws_ram_resource_share_associations` | `AWS::Ram::ResourceShareAssociation` | generic | +| ram | `aws_ram_resource_share_invitations` | `AWS::Ram::ResourceShareInvitation` | generic | +| ram | `aws_ram_resource_share_permissions` | `AWS::Ram::ResourceSharePermission` | generic | +| ram | `aws_ram_resource_shares` | `AWS::Ram::ResourceShare` | generic | +| ram | `aws_ram_resource_types` | `AWS::Ram::ResourceType` | generic | +| ram | `aws_ram_resources` | `AWS::Ram::Resource` | generic | +| rds | `aws_rds_certificates` | `AWS::RDS::Certificate` | generic | +| rds | `aws_rds_cluster_backtracks` | `AWS::RDS::ClusterBacktrack` | generic | +| rds | `aws_rds_cluster_parameter_group_parameters` | `AWS::RDS::ClusterParameterGroupParameter` | generic | +| rds | `aws_rds_cluster_parameter_groups` | `AWS::RDS::ClusterParameterGroup` | generic | +| rds | `aws_rds_cluster_parameters` | `AWS::RDS::ClusterParameter` | generic | +| rds | `aws_rds_cluster_snapshots` | `AWS::RDS::ClusterSnapshot` | generic | +| rds | `aws_rds_clusters` | `AWS::RDS::DBCluster` | generic | +| rds | `aws_rds_db_parameter_group_db_parameters` | `AWS::RDS::DbParameterGroupDbParameter` | generic | +| rds | `aws_rds_db_parameter_groups` | `AWS::RDS::DbParameterGroup` | generic | +| rds | `aws_rds_db_proxies` | `AWS::RDS::DBProxy` | generic | +| rds | `aws_rds_db_proxy_endpoints` | `AWS::RDS::DbProxyEndpoint` | generic | +| rds | `aws_rds_db_proxy_target_groups` | `AWS::RDS::DbProxyTargetGroup` | generic | +| rds | `aws_rds_db_proxy_targets` | `AWS::RDS::DbProxyTarget` | generic | +| rds | `aws_rds_db_security_groups` | `AWS::RDS::DbSecurityGroup` | generic | +| rds | `aws_rds_db_snapshots` | `AWS::RDS::DbSnapshot` | generic | +| rds | `aws_rds_engine_versions` | `AWS::RDS::EngineVersion` | generic | +| rds | `aws_rds_event_subscriptions` | `AWS::RDS::EventSubscription` | generic | +| rds | `aws_rds_events` | `AWS::RDS::Event` | generic | +| rds | `aws_rds_global_clusters` | `AWS::RDS::GlobalCluster` | generic | +| rds | `aws_rds_instance_resource_metrics` | `AWS::RDS::InstanceResourceMetric` | generic | +| rds | `aws_rds_instances` | `AWS::RDS::DBInstance` | generic | +| rds | `aws_rds_major_engine_versions` | `AWS::RDS::MajorEngineVersion` | generic | +| rds | `aws_rds_option_groups` | `AWS::RDS::OptionGroup` | generic | +| rds | `aws_rds_pending_maintenance_actions` | `AWS::RDS::PendingMaintenanceAction` | generic | +| rds | `aws_rds_reserved_instances` | `AWS::RDS::ReservedInstance` | generic | +| rds | `aws_rds_subnet_groups` | `AWS::RDS::DBSubnetGroup` | generic | +| redshift | `aws_redshift_cluster_parameter_groups` | `AWS::Redshift::ClusterParameterGroup` | generic | +| redshift | `aws_redshift_cluster_parameters` | `AWS::Redshift::ClusterParameter` | generic | +| redshift | `aws_redshift_clusters` | `AWS::Redshift::Cluster` | generic | +| redshift | `aws_redshift_data_shares` | `AWS::Redshift::DataShare` | generic | +| redshift | `aws_redshift_endpoint_accesses` | `AWS::Redshift::EndpointAccess` | generic | +| redshift | `aws_redshift_endpoint_authorizations` | `AWS::Redshift::EndpointAuthorization` | generic | +| redshift | `aws_redshift_event_subscriptions` | `AWS::Redshift::EventSubscription` | generic | +| redshift | `aws_redshift_events` | `AWS::Redshift::Event` | generic | +| redshift | `aws_redshift_reserved_nodes` | `AWS::Redshift::ReservedNode` | generic | +| redshift | `aws_redshift_snapshots` | `AWS::Redshift::Snapshot` | generic | +| redshift | `aws_redshift_subnet_groups` | `AWS::Redshift::SubnetGroup` | generic | +| regions | `aws_regions` | `-` | generic | +| rekognition | `aws_rekognition_collection_faces` | `AWS::Rekognition::CollectionFace` | generic | +| rekognition | `aws_rekognition_collections` | `AWS::Rekognition::Collection` | generic | +| rekognition | `aws_rekognition_media_analysis_jobs` | `AWS::Rekognition::MediaAnalysisJob` | generic | +| rekognition | `aws_rekognition_project_versions` | `AWS::Rekognition::ProjectVersion` | generic | +| rekognition | `aws_rekognition_projects` | `AWS::Rekognition::Project` | generic | +| rekognition | `aws_rekognition_stream_processors` | `AWS::Rekognition::StreamProcessor` | generic | +| resiliencehub | `aws_resiliencehub_alarm_recommendations` | `AWS::ResilienceHub::AlarmRecommendation` | generic | +| resiliencehub | `aws_resiliencehub_app_assessments` | `AWS::ResilienceHub::AppAssessment` | generic | +| resiliencehub | `aws_resiliencehub_app_component_compliances` | `AWS::ResilienceHub::AppComponentCompliance` | generic | +| resiliencehub | `aws_resiliencehub_app_version_resource_mappings` | `AWS::ResilienceHub::AppVersionResourceMapping` | generic | +| resiliencehub | `aws_resiliencehub_app_version_resources` | `AWS::ResilienceHub::AppVersionResource` | generic | +| resiliencehub | `aws_resiliencehub_app_versions` | `AWS::ResilienceHub::AppVersion` | generic | +| resiliencehub | `aws_resiliencehub_apps` | `AWS::ResilienceHub::App` | generic | +| resiliencehub | `aws_resiliencehub_component_recommendations` | `AWS::ResilienceHub::ComponentRecommendation` | generic | +| resiliencehub | `aws_resiliencehub_recommendation_templates` | `AWS::ResilienceHub::RecommendationTemplate` | generic | +| resiliencehub | `aws_resiliencehub_resiliency_policies` | `AWS::ResilienceHub::ResiliencyPolicy` | generic | +| resiliencehub | `aws_resiliencehub_sop_recommendations` | `AWS::ResilienceHub::SopRecommendation` | generic | +| resiliencehub | `aws_resiliencehub_suggested_resiliency_policies` | `AWS::ResilienceHub::SuggestedResiliencyPolicy` | generic | +| resiliencehub | `aws_resiliencehub_test_recommendations` | `AWS::ResilienceHub::TestRecommendation` | generic | +| resourcegroups | `aws_resourcegroups_resource_groups` | `AWS::ResourceGroups::ResourceGroup` | generic | +| route53 | `aws_route53_delegation_sets` | `AWS::Route53::DelegationSet` | generic | +| route53 | `aws_route53_domains` | `AWS::Route53::Domain` | generic | +| route53 | `aws_route53_health_checks` | `AWS::Route53::HealthCheck` | generic | +| route53 | `aws_route53_hosted_zone_dnssecs` | `AWS::Route53::HostedZoneDnssec` | generic | +| route53 | `aws_route53_hosted_zone_query_logging_configs` | `AWS::Route53::HostedZoneQueryLoggingConfig` | generic | +| route53 | `aws_route53_hosted_zone_resource_record_sets` | `AWS::Route53::HostedZoneResourceRecordSet` | generic | +| route53 | `aws_route53_hosted_zone_traffic_policy_instances` | `AWS::Route53::HostedZoneTrafficPolicyInstance` | generic | +| route53 | `aws_route53_hosted_zones` | `AWS::Route53::HostedZone` | generic | +| route53 | `aws_route53_operations` | `AWS::Route53::Operation` | generic | +| route53 | `aws_route53_profiles` | `AWS::Route53::Profile` | generic | +| route53 | `aws_route53_traffic_policies` | `AWS::Route53::TrafficPolicy` | generic | +| route53 | `aws_route53_traffic_policy_versions` | `AWS::Route53::TrafficPolicyVersion` | generic | +| route53recoverycontrolconfig | `aws_route53recoverycontrolconfig_clusters` | `AWS::Route53RecoveryControl::Cluster` | generic | +| route53recoverycontrolconfig | `aws_route53recoverycontrolconfig_control_panels` | `AWS::Route53RecoveryControl::ControlPanel` | generic | +| route53recoverycontrolconfig | `aws_route53recoverycontrolconfig_routing_controls` | `AWS::Route53RecoveryControl::RoutingControl` | generic | +| route53recoverycontrolconfig | `aws_route53recoverycontrolconfig_safety_rules` | `AWS::Route53RecoveryControl::SafetyRule` | generic | +| route53recoveryreadiness | `aws_route53recoveryreadiness_cells` | `AWS::Route53RecoveryReadiness::Cell` | generic | +| route53recoveryreadiness | `aws_route53recoveryreadiness_readiness_checks` | `AWS::Route53RecoveryReadiness::ReadinessCheck` | generic | +| route53recoveryreadiness | `aws_route53recoveryreadiness_recovery_groups` | `AWS::Route53RecoveryReadiness::RecoveryGroup` | generic | +| route53recoveryreadiness | `aws_route53recoveryreadiness_resource_sets` | `AWS::Route53RecoveryReadiness::ResourceSet` | generic | +| route53resolver | `aws_route53resolver_firewall_configs` | `AWS::Route53Resolver::FirewallConfig` | generic | +| route53resolver | `aws_route53resolver_firewall_domain_lists` | `AWS::Route53Resolver::FirewallDomainList` | generic | +| route53resolver | `aws_route53resolver_firewall_rule_group_associations` | `AWS::Route53Resolver::FirewallRuleGroupAssociation` | generic | +| route53resolver | `aws_route53resolver_firewall_rule_groups` | `AWS::Route53Resolver::FirewallRuleGroup` | generic | +| route53resolver | `aws_route53resolver_resolver_endpoints` | `AWS::Route53Resolver::ResolverEndpoint` | generic | +| route53resolver | `aws_route53resolver_resolver_query_log_config_associations` | `AWS::Route53Resolver::ResolverQueryLogConfigAssociation` | generic | +| route53resolver | `aws_route53resolver_resolver_query_log_configs` | `AWS::Route53Resolver::ResolverQueryLogConfig` | generic | +| route53resolver | `aws_route53resolver_resolver_rule_associations` | `AWS::Route53Resolver::ResolverRuleAssociation` | generic | +| route53resolver | `aws_route53resolver_resolver_rules` | `AWS::Route53Resolver::ResolverRule` | generic | +| s3 | `aws_s3_access_grant_instances` | `AWS::S3::AccessGrantInstance` | generic | +| s3 | `aws_s3_access_grants` | `AWS::S3::AccessGrant` | generic | +| s3 | `aws_s3_access_points` | `AWS::S3::AccessPoint` | generic | +| s3 | `aws_s3_accounts` | `AWS::S3::Account` | generic | +| s3 | `aws_s3_bucket_cors_rules` | `AWS::S3::BucketCorsRule` | generic | +| s3 | `aws_s3_bucket_encryption_rules` | `AWS::S3::BucketEncryptionRule` | generic | +| s3 | `aws_s3_bucket_grants` | `AWS::S3::BucketGrant` | generic | +| s3 | `aws_s3_bucket_lifecycles` | `AWS::S3::BucketLifecycle` | generic | +| s3 | `aws_s3_bucket_loggings` | `AWS::S3::BucketLogging` | generic | +| s3 | `aws_s3_bucket_notification_configurations` | `AWS::S3::BucketNotificationConfiguration` | generic | +| s3 | `aws_s3_bucket_object_grants` | `AWS::S3::BucketObjectGrant` | generic | +| s3 | `aws_s3_bucket_object_heads` | `AWS::S3::BucketObjectHead` | generic | +| s3 | `aws_s3_bucket_object_lock_configurations` | `AWS::S3::BucketObjectLockConfiguration` | generic | +| s3 | `aws_s3_bucket_objects` | `AWS::S3::BucketObject` | generic | +| s3 | `aws_s3_bucket_ownership_controls` | `AWS::S3::BucketOwnershipControl` | generic | +| s3 | `aws_s3_bucket_policies` | `AWS::S3::BucketPolicy` | generic | +| s3 | `aws_s3_bucket_public_access_blocks` | `AWS::S3::BucketPublicAccessBlock` | generic | +| s3 | `aws_s3_bucket_replications` | `AWS::S3::BucketReplication` | generic | +| s3 | `aws_s3_bucket_versionings` | `AWS::S3::BucketVersioning` | generic | +| s3 | `aws_s3_bucket_websites` | `AWS::S3::BucketWebsite` | generic | +| s3 | `aws_s3_buckets` | `AWS::S3::Bucket` | typed | +| s3 | `aws_s3_directory_buckets` | `AWS::S3::DirectoryBucket` | generic | +| s3 | `aws_s3_multi_region_access_points` | `AWS::S3::MultiRegionAccessPoint` | generic | +| s3 | `aws_s3_storage_lens_configurations` | `AWS::S3::StorageLensConfiguration` | generic | +| s3 | `aws_s3_storage_lens_groups` | `AWS::S3::StorageLensGroup` | generic | +| s3tables | `aws_s3tables_bucket_policies` | `AWS::S3tables::BucketPolicy` | generic | +| s3tables | `aws_s3tables_buckets` | `AWS::S3tables::Bucket` | generic | +| s3tables | `aws_s3tables_namespaces` | `AWS::S3tables::Namespace` | generic | +| s3vectors | `aws_s3vectors_bucket_policies` | `AWS::S3vectors::BucketPolicy` | generic | +| s3vectors | `aws_s3vectors_buckets` | `AWS::S3vectors::Bucket` | generic | +| s3vectors | `aws_s3vectors_indexes` | `AWS::S3vectors::Index` | generic | +| sagemaker | `aws_sagemaker_apps` | `AWS::SageMaker::App` | generic | +| sagemaker | `aws_sagemaker_domains` | `AWS::SageMaker::Domain` | generic | +| sagemaker | `aws_sagemaker_endpoint_configurations` | `AWS::SageMaker::EndpointConfiguration` | generic | +| sagemaker | `aws_sagemaker_endpoints` | `AWS::SageMaker::Endpoint` | generic | +| sagemaker | `aws_sagemaker_hyperparameter_tuning_jobs` | `AWS::SageMaker::HyperparameterTuningJob` | generic | +| sagemaker | `aws_sagemaker_image_versions` | `AWS::SageMaker::ImageVersion` | generic | +| sagemaker | `aws_sagemaker_images` | `AWS::SageMaker::Image` | generic | +| sagemaker | `aws_sagemaker_mlflow_apps` | `AWS::SageMaker::MlflowApp` | generic | +| sagemaker | `aws_sagemaker_mlflow_tracking_servers` | `AWS::SageMaker::MlflowTrackingServer` | generic | +| sagemaker | `aws_sagemaker_models` | `AWS::SageMaker::Model` | generic | +| sagemaker | `aws_sagemaker_notebook_instance_lifecycle_configs` | `AWS::SageMaker::NotebookInstanceLifecycleConfig` | generic | +| sagemaker | `aws_sagemaker_notebook_instances` | `AWS::SageMaker::NotebookInstance` | generic | +| sagemaker | `aws_sagemaker_processing_jobs` | `AWS::SageMaker::ProcessingJob` | generic | +| sagemaker | `aws_sagemaker_spaces` | `AWS::SageMaker::Space` | generic | +| sagemaker | `aws_sagemaker_studio_lifecycle_configs` | `AWS::SageMaker::StudioLifecycleConfig` | generic | +| sagemaker | `aws_sagemaker_training_jobs` | `AWS::SageMaker::TrainingJob` | generic | +| sagemaker | `aws_sagemaker_transform_jobs` | `AWS::SageMaker::TransformJob` | generic | +| sagemaker | `aws_sagemaker_user_profiles` | `AWS::SageMaker::UserProfile` | generic | +| savingsplans | `aws_savingsplans_plans` | `AWS::SavingsPlans::Plan` | generic | +| scheduler | `aws_scheduler_schedule_groups` | `AWS::Scheduler::ScheduleGroup` | generic | +| scheduler | `aws_scheduler_schedules` | `AWS::Scheduler::Schedule` | generic | +| secretsmanager | `aws_secretsmanager_secret_versions` | `AWS::SecretsManager::SecretVersion` | generic | +| secretsmanager | `aws_secretsmanager_secrets` | `AWS::SecretsManager::Secret` | generic | +| securityhub | `aws_securityhub_enabled_standards` | `AWS::SecurityHub::EnabledStandard` | generic | +| securityhub | `aws_securityhub_findings` | `AWS::SecurityHub::Finding` | generic | +| securityhub | `aws_securityhub_hubs` | `AWS::SecurityHub::Hub` | generic | +| servicecatalog | `aws_servicecatalog_launch_paths` | `AWS::ServiceCatalog::LaunchPath` | generic | +| servicecatalog | `aws_servicecatalog_portfolios` | `AWS::ServiceCatalog::Portfolio` | generic | +| servicecatalog | `aws_servicecatalog_products` | `AWS::ServiceCatalog::Product` | generic | +| servicecatalog | `aws_servicecatalog_provisioned_products` | `AWS::ServiceCatalog::ProvisionedProduct` | generic | +| servicecatalog | `aws_servicecatalog_provisioning_artifacts` | `AWS::ServiceCatalog::ProvisioningArtifact` | generic | +| servicecatalog | `aws_servicecatalog_provisioning_parameters` | `AWS::ServiceCatalog::ProvisioningParameter` | generic | +| servicediscovery | `aws_servicediscovery_instances` | `AWS::ServiceDiscovery::Instance` | generic | +| servicediscovery | `aws_servicediscovery_namespaces` | `AWS::ServiceDiscovery::Namespace` | generic | +| servicediscovery | `aws_servicediscovery_services` | `AWS::ServiceDiscovery::Service` | generic | +| servicequotas | `aws_servicequotas_awsdefaultservicequotas` | `AWS::ServiceQuotas::Awsdefaultservicequota` | generic | +| servicequotas | `aws_servicequotas_quota_utilizations` | `AWS::ServiceQuotas::QuotaUtilization` | generic | +| servicequotas | `aws_servicequotas_quotas` | `AWS::ServiceQuotas::Quota` | generic | +| servicequotas | `aws_servicequotas_services` | `AWS::ServiceQuotas::Service` | generic | +| ses | `aws_ses_active_receipt_rule_sets` | `AWS::SES::ActiveReceiptRuleSet` | generic | +| ses | `aws_ses_configuration_set_event_destinations` | `AWS::SES::ConfigurationSetEventDestination` | generic | +| ses | `aws_ses_configuration_sets` | `AWS::SES::ConfigurationSet` | generic | +| ses | `aws_ses_contact_lists` | `AWS::SES::ContactList` | generic | +| ses | `aws_ses_custom_verification_email_templates` | `AWS::SES::CustomVerificationEmailTemplate` | generic | +| ses | `aws_ses_identities` | `AWS::SES::Identity` | generic | +| ses | `aws_ses_suppressed_destinations` | `AWS::SES::SuppressedDestination` | generic | +| ses | `aws_ses_templates` | `AWS::SES::Template` | generic | +| shield | `aws_shield_attacks` | `AWS::Shield::Attack` | generic | +| shield | `aws_shield_protection_groups` | `AWS::Shield::ProtectionGroup` | generic | +| shield | `aws_shield_protections` | `AWS::Shield::Protection` | generic | +| shield | `aws_shield_subscriptions` | `AWS::Shield::Subscription` | generic | +| signer | `aws_signer_signing_profiles` | `AWS::Signer::SigningProfile` | generic | +| snowball | `aws_snowball_addresses` | `AWS::Snowball::Address` | generic | +| snowball | `aws_snowball_cluster_jobs` | `AWS::Snowball::ClusterJob` | generic | +| snowball | `aws_snowball_clusters` | `AWS::Snowball::Cluster` | generic | +| snowball | `aws_snowball_compatible_images` | `AWS::Snowball::CompatibleImage` | generic | +| snowball | `aws_snowball_jobs` | `AWS::Snowball::Job` | generic | +| snowball | `aws_snowball_long_term_pricing` | `AWS::Snowball::LongTermPricing` | generic | +| snowball | `aws_snowball_pickup_locations` | `AWS::Snowball::PickupLocation` | generic | +| sns | `aws_sns_subscriptions` | `AWS::SNS::Subscription` | generic | +| sns | `aws_sns_topic_data_protection_policies` | `AWS::SNS::TopicDataProtectionPolicy` | generic | +| sns | `aws_sns_topics` | `AWS::SNS::Topic` | generic | +| sqs | `aws_sqs_queues` | `AWS::SQS::Queue` | generic | +| ssm | `aws_ssm_associations` | `AWS::SSM::Association` | generic | +| ssm | `aws_ssm_command_invocations` | `AWS::SSM::CommandInvocation` | generic | +| ssm | `aws_ssm_compliance_summary_items` | `AWS::SSM::ComplianceSummaryItem` | generic | +| ssm | `aws_ssm_document_contents` | `AWS::SSM::DocumentContent` | generic | +| ssm | `aws_ssm_document_versions` | `AWS::SSM::DocumentVersion` | generic | +| ssm | `aws_ssm_documents` | `AWS::SSM::Document` | generic | +| ssm | `aws_ssm_instance_compliance_items` | `AWS::SSM::InstanceComplianceItem` | generic | +| ssm | `aws_ssm_instance_patch_states` | `AWS::SSM::InstancePatchState` | generic | +| ssm | `aws_ssm_instance_patches` | `AWS::SSM::InstancePatch` | generic | +| ssm | `aws_ssm_instances` | `AWS::SSM::Instance` | generic | +| ssm | `aws_ssm_inventories` | `AWS::SSM::Inventory` | generic | +| ssm | `aws_ssm_inventory_entries` | `AWS::SSM::InventoryEntry` | generic | +| ssm | `aws_ssm_inventory_schemas` | `AWS::SSM::InventorySchema` | generic | +| ssm | `aws_ssm_maintenance_window_executions` | `AWS::SSM::MaintenanceWindowExecution` | generic | +| ssm | `aws_ssm_maintenance_window_schedules` | `AWS::SSM::MaintenanceWindowSchedule` | generic | +| ssm | `aws_ssm_maintenance_window_targets` | `AWS::SSM::MaintenanceWindowTarget` | generic | +| ssm | `aws_ssm_maintenance_window_tasks` | `AWS::SSM::MaintenanceWindowTask` | generic | +| ssm | `aws_ssm_maintenance_windows` | `AWS::SSM::MaintenanceWindow` | generic | +| ssm | `aws_ssm_parameters` | `AWS::SSM::Parameter` | generic | +| ssm | `aws_ssm_patch_baselines` | `AWS::SSM::PatchBaseline` | generic | +| ssm | `aws_ssm_sessions` | `AWS::SSM::Session` | generic | +| ssmincidents | `aws_ssmincidents_incident_findings` | `AWS::SSMIncidents::IncidentFinding` | generic | +| ssmincidents | `aws_ssmincidents_incident_related_items` | `AWS::SSMIncidents::IncidentRelatedItem` | generic | +| ssmincidents | `aws_ssmincidents_incident_timeline_events` | `AWS::SSMIncidents::IncidentTimelineEvent` | generic | +| ssmincidents | `aws_ssmincidents_incidents` | `AWS::SSMIncidents::Incident` | generic | +| ssmincidents | `aws_ssmincidents_response_plans` | `AWS::SSMIncidents::ResponsePlan` | generic | +| ssoadmin | `aws_ssoadmin_instances` | `AWS::SSO::Instance` | generic | +| ssoadmin | `aws_ssoadmin_permission_set_account_assignments` | `AWS::SSO::PermissionSetAccountAssignment` | generic | +| ssoadmin | `aws_ssoadmin_permission_set_customer_managed_policies` | `AWS::SSO::PermissionSetCustomerManagedPolicy` | generic | +| ssoadmin | `aws_ssoadmin_permission_set_inline_policies` | `AWS::SSO::PermissionSetInlinePolicy` | generic | +| ssoadmin | `aws_ssoadmin_permission_set_managed_policies` | `AWS::SSO::PermissionSetManagedPolicy` | generic | +| ssoadmin | `aws_ssoadmin_permission_set_permissions_boundaries` | `AWS::SSO::PermissionSetPermissionsBoundary` | generic | +| ssoadmin | `aws_ssoadmin_permission_sets` | `AWS::SSO::PermissionSet` | generic | +| ssoadmin | `aws_ssoadmin_trusted_token_issuers` | `AWS::SSO::TrustedTokenIssuer` | generic | +| stepfunctions | `aws_stepfunctions_activities` | `AWS::Stepfunctions::Activity` | generic | +| stepfunctions | `aws_stepfunctions_executions` | `AWS::Stepfunctions::Execution` | generic | +| stepfunctions | `aws_stepfunctions_map_run_executions` | `AWS::Stepfunctions::MapRunExecution` | generic | +| stepfunctions | `aws_stepfunctions_map_runs` | `AWS::Stepfunctions::MapRun` | generic | +| stepfunctions | `aws_stepfunctions_state_machines` | `AWS::StepFunctions::StateMachine` | generic | +| storagegateway | `aws_storagegateway_automatic_tape_creation_policies` | `AWS::StorageGateway::AutomaticTapeCreationPolicy` | generic | +| storagegateway | `aws_storagegateway_cache_reports` | `AWS::StorageGateway::CacheReport` | generic | +| storagegateway | `aws_storagegateway_file_shares` | `AWS::StorageGateway::FileShare` | generic | +| storagegateway | `aws_storagegateway_file_system_associations` | `AWS::StorageGateway::FileSystemAssociation` | generic | +| storagegateway | `aws_storagegateway_gateways` | `AWS::StorageGateway::Gateway` | generic | +| storagegateway | `aws_storagegateway_local_disks` | `AWS::StorageGateway::LocalDisk` | generic | +| storagegateway | `aws_storagegateway_tape_pools` | `AWS::StorageGateway::TapePool` | generic | +| storagegateway | `aws_storagegateway_tapes` | `AWS::StorageGateway::Tape` | generic | +| storagegateway | `aws_storagegateway_volume_recovery_points` | `AWS::StorageGateway::VolumeRecoveryPoint` | generic | +| storagegateway | `aws_storagegateway_volumes` | `AWS::StorageGateway::Volume` | generic | +| support | `aws_support_case_communications` | `AWS::Support::CaseCommunication` | generic | +| support | `aws_support_cases` | `AWS::Support::Cas` | generic | +| support | `aws_support_services` | `AWS::Support::Service` | generic | +| support | `aws_support_severity_levels` | `AWS::Support::SeverityLevel` | generic | +| support | `aws_support_trusted_advisor_check_results` | `AWS::Support::TrustedAdvisorCheckResult` | generic | +| support | `aws_support_trusted_advisor_check_summaries` | `AWS::Support::TrustedAdvisorCheckSummary` | generic | +| support | `aws_support_trusted_advisor_checks` | `AWS::Support::TrustedAdvisorCheck` | generic | +| swf | `aws_swf_activity_types` | `AWS::SWF::ActivityType` | generic | +| swf | `aws_swf_closed_workflow_executions` | `AWS::SWF::ClosedWorkflowExecution` | generic | +| swf | `aws_swf_domains` | `AWS::SWF::Domain` | generic | +| swf | `aws_swf_open_workflow_executions` | `AWS::SWF::OpenWorkflowExecution` | generic | +| swf | `aws_swf_workflow_types` | `AWS::SWF::WorkflowType` | generic | +| timestream | `aws_timestream_databases` | `AWS::Timestream::Databas` | generic | +| timestream | `aws_timestream_tables` | `AWS::Timestream::Table` | generic | +| transcribe | `aws_transcribe_call_analytics_categories` | `AWS::Transcribe::CallAnalyticsCategory` | generic | +| transcribe | `aws_transcribe_call_analytics_jobs` | `AWS::Transcribe::CallAnalyticsJob` | generic | +| transcribe | `aws_transcribe_language_models` | `AWS::Transcribe::LanguageModel` | generic | +| transcribe | `aws_transcribe_medical_scribe_jobs` | `AWS::Transcribe::MedicalScribeJob` | generic | +| transcribe | `aws_transcribe_medical_transcription_jobs` | `AWS::Transcribe::MedicalTranscriptionJob` | generic | +| transcribe | `aws_transcribe_medical_vocabularies` | `AWS::Transcribe::MedicalVocabulary` | generic | +| transcribe | `aws_transcribe_transcription_jobs` | `AWS::Transcribe::TranscriptionJob` | generic | +| transcribe | `aws_transcribe_vocabularies` | `AWS::Transcribe::Vocabulary` | generic | +| transcribe | `aws_transcribe_vocabulary_filters` | `AWS::Transcribe::VocabularyFilter` | generic | +| transfer | `aws_transfer_agreements` | `AWS::Transfer::Agreement` | generic | +| transfer | `aws_transfer_certificates` | `AWS::Transfer::Certificate` | generic | +| transfer | `aws_transfer_connectors` | `AWS::Transfer::Connector` | generic | +| transfer | `aws_transfer_profiles` | `AWS::Transfer::Profile` | generic | +| transfer | `aws_transfer_servers` | `AWS::Transfer::Server` | generic | +| transfer | `aws_transfer_users` | `AWS::Transfer::User` | generic | +| transfer | `aws_transfer_workflows` | `AWS::Transfer::Workflow` | generic | +| trustedadvisor | `aws_trustedadvisor_organization_recommendation_accounts` | `AWS::Trustedadvisor::OrganizationRecommendationAccount` | generic | +| trustedadvisor | `aws_trustedadvisor_organization_recommendation_resources` | `AWS::Trustedadvisor::OrganizationRecommendationResource` | generic | +| trustedadvisor | `aws_trustedadvisor_organization_recommendations` | `AWS::Trustedadvisor::OrganizationRecommendation` | generic | +| trustedadvisor | `aws_trustedadvisor_recommendation_resources` | `AWS::Trustedadvisor::RecommendationResource` | generic | +| trustedadvisor | `aws_trustedadvisor_recommendations` | `AWS::Trustedadvisor::Recommendation` | generic | +| vpc | `aws_vpc_lattice_resource_configurations` | `AWS::Vpc::LatticeResourceConfiguration` | generic | +| vpc | `aws_vpc_lattice_resource_gateways` | `AWS::Vpc::LatticeResourceGateway` | generic | +| vpc | `aws_vpc_lattice_service_networks` | `AWS::Vpc::LatticeServiceNetwork` | generic | +| vpc | `aws_vpc_lattice_services` | `AWS::Vpc::LatticeService` | generic | +| waf | `aws_waf_ipsets` | `AWS::WAF::Ipset` | generic | +| waf | `aws_waf_rule_groups` | `AWS::WAF::RuleGroup` | generic | +| waf | `aws_waf_rules` | `AWS::WAF::Rule` | generic | +| waf | `aws_waf_subscribed_rule_groups` | `AWS::WAF::SubscribedRuleGroup` | generic | +| waf | `aws_waf_web_acls` | `AWS::WAF::WebAcl` | generic | +| wafregional | `aws_wafregional_rate_based_rules` | `AWS::WAFRegional::RateBasedRule` | generic | +| wafregional | `aws_wafregional_rule_groups` | `AWS::WAFRegional::RuleGroup` | generic | +| wafregional | `aws_wafregional_rules` | `AWS::WAFRegional::Rule` | generic | +| wafregional | `aws_wafregional_web_acls` | `AWS::WAFRegional::WebAcl` | generic | +| wafv2 | `aws_wafv2_ipsets` | `AWS::WAFv2::Ipset` | generic | +| wafv2 | `aws_wafv2_managed_rule_groups` | `AWS::WAFv2::ManagedRuleGroup` | generic | +| wafv2 | `aws_wafv2_regex_pattern_sets` | `AWS::WAFv2::RegexPatternSet` | generic | +| wafv2 | `aws_wafv2_rule_groups` | `AWS::WAFv2::RuleGroup` | generic | +| wafv2 | `aws_wafv2_web_acls` | `AWS::WAFv2::WebACL` | generic | +| wellarchitected | `aws_wellarchitected_lens_review_improvements` | `AWS::WellArchitected::LensReviewImprovement` | generic | +| wellarchitected | `aws_wellarchitected_lens_reviews` | `AWS::WellArchitected::LensReview` | generic | +| wellarchitected | `aws_wellarchitected_lenses` | `AWS::WellArchitected::Lens` | generic | +| wellarchitected | `aws_wellarchitected_share_invitations` | `AWS::WellArchitected::ShareInvitation` | generic | +| wellarchitected | `aws_wellarchitected_workload_milestones` | `AWS::WellArchitected::WorkloadMilestone` | generic | +| wellarchitected | `aws_wellarchitected_workload_shares` | `AWS::WellArchitected::WorkloadShare` | generic | +| wellarchitected | `aws_wellarchitected_workloads` | `AWS::WellArchitected::Workload` | generic | +| workspaces | `aws_workspaces_connection_alias_permissions` | `AWS::WorkSpaces::ConnectionAliasPermission` | generic | +| workspaces | `aws_workspaces_connection_aliases` | `AWS::WorkSpaces::ConnectionAlias` | generic | +| workspaces | `aws_workspaces_directories` | `AWS::WorkSpaces::Directory` | generic | +| workspaces | `aws_workspaces_workspaces` | `AWS::WorkSpaces::Workspace` | generic | +| xray | `aws_xray_encryption_configs` | `AWS::XRay::EncryptionConfig` | generic | +| xray | `aws_xray_groups` | `AWS::XRay::Group` | generic | +| xray | `aws_xray_resource_policies` | `AWS::XRay::ResourcePolicy` | generic | +| xray | `aws_xray_sampling_rules` | `AWS::XRay::SamplingRule` | generic | + diff --git a/docs/authoring/indexed-resources/azure-resource-catalog.md b/docs/authoring/indexed-resources/azure-resource-catalog.md index 7b89ecd8..87b562d0 100644 --- a/docs/authoring/indexed-resources/azure-resource-catalog.md +++ b/docs/authoring/indexed-resources/azure-resource-catalog.md @@ -2,7 +2,7 @@ Every Azure resource type the native `azureapi` indexer can discover. This page is the companion catalog for [`azure.md`](./azure.md); see that page for how to enable the indexer, what data each row carries, and the typed/generic distinction. -_619 resource types - 25 typed (rich-payload), 594 generic (basic envelope). Generated 2026-05-28 from `src/indexers/azure_resource_type_registry.yaml`._ +_619 resource types - 25 typed (rich-payload), 594 generic (basic envelope). Generated 2026-07-02 from `src/indexers/azure_resource_type_registry.yaml`._ _Regenerate with `python scripts/azure/dump_azure_resource_catalog.py` after touching the registry or overrides; do not hand-edit this file._ diff --git a/docs/authoring/indexed-resources/gcp-resource-catalog.md b/docs/authoring/indexed-resources/gcp-resource-catalog.md new file mode 100644 index 00000000..473c44e9 --- /dev/null +++ b/docs/authoring/indexed-resources/gcp-resource-catalog.md @@ -0,0 +1,418 @@ +# GCP resource catalog + +Every GCP resource type the native `gcpapi` indexer can discover. This page is the companion catalog for [`gcp.md`](./gcp.md); see that page for how to enable the indexer, what data each row carries, and the typed/generic distinction. + +_404 resource types - 13 typed (SDK collectors), 391 generic (Cloud Asset Inventory pass). Generated 2026-07-02 from `src/indexers/gcp_resource_type_registry.yaml`._ + +_Regenerate with `python scripts/gcp/dump_gcp_resource_catalog.py` after touching the registry or overrides; do not hand-edit this file._ + +* `typed` - hand-written `google-cloud-*` collector; runs without CAI. +* `generic` - discoverable via the optional Cloud Asset Inventory accelerator when a CAI asset type is mapped. + +| Service | CloudQuery table name | CAI asset type | Tier | +| --- | --- | --- | --- | +| accessapproval | `gcp_accessapproval_folder_approval_requests` | `accessapproval.googleapis.com/FolderApprovalRequest` | generic | +| accessapproval | `gcp_accessapproval_folder_service_accounts` | `accessapproval.googleapis.com/FolderServiceAccount` | generic | +| accessapproval | `gcp_accessapproval_folder_settings` | `accessapproval.googleapis.com/FolderSetting` | generic | +| accessapproval | `gcp_accessapproval_organization_approval_requests` | `accessapproval.googleapis.com/OrganizationApprovalRequest` | generic | +| accessapproval | `gcp_accessapproval_organization_service_accounts` | `accessapproval.googleapis.com/OrganizationServiceAccount` | generic | +| accessapproval | `gcp_accessapproval_organization_settings` | `accessapproval.googleapis.com/OrganizationSetting` | generic | +| accessapproval | `gcp_accessapproval_project_approval_requests` | `accessapproval.googleapis.com/ProjectApprovalRequest` | generic | +| accessapproval | `gcp_accessapproval_project_service_accounts` | `accessapproval.googleapis.com/ProjectServiceAccount` | generic | +| accessapproval | `gcp_accessapproval_project_settings` | `accessapproval.googleapis.com/ProjectSetting` | generic | +| aiplatform | `gcp_aiplatform_batch_prediction_jobs` | `aiplatform.googleapis.com/BatchPredictionJob` | generic | +| aiplatform | `gcp_aiplatform_custom_jobs` | `aiplatform.googleapis.com/CustomJob` | generic | +| aiplatform | `gcp_aiplatform_dataset_locations` | `aiplatform.googleapis.com/DatasetLocation` | generic | +| aiplatform | `gcp_aiplatform_datasets` | `aiplatform.googleapis.com/Dataset` | generic | +| aiplatform | `gcp_aiplatform_endpoint_locations` | `aiplatform.googleapis.com/EndpointLocation` | generic | +| aiplatform | `gcp_aiplatform_endpoints` | `aiplatform.googleapis.com/Endpoint` | generic | +| aiplatform | `gcp_aiplatform_featurestore_locations` | `aiplatform.googleapis.com/FeaturestoreLocation` | generic | +| aiplatform | `gcp_aiplatform_featurestores` | `aiplatform.googleapis.com/Featurestore` | generic | +| aiplatform | `gcp_aiplatform_hyperparameter_tuning_jobs` | `aiplatform.googleapis.com/HyperparameterTuningJob` | generic | +| aiplatform | `gcp_aiplatform_index_endpoints` | `aiplatform.googleapis.com/IndexEndpoint` | generic | +| aiplatform | `gcp_aiplatform_index_locations` | `aiplatform.googleapis.com/IndexLocation` | generic | +| aiplatform | `gcp_aiplatform_indexendpoint_locations` | `aiplatform.googleapis.com/IndexendpointLocation` | generic | +| aiplatform | `gcp_aiplatform_indexes` | `aiplatform.googleapis.com/Index` | generic | +| aiplatform | `gcp_aiplatform_job_locations` | `aiplatform.googleapis.com/JobLocation` | generic | +| aiplatform | `gcp_aiplatform_metadata_locations` | `aiplatform.googleapis.com/MetadataLocation` | generic | +| aiplatform | `gcp_aiplatform_metadata_stores` | `aiplatform.googleapis.com/MetadataStore` | generic | +| aiplatform | `gcp_aiplatform_model_deployment_monitoring_jobs` | `aiplatform.googleapis.com/ModelDeploymentMonitoringJob` | generic | +| aiplatform | `gcp_aiplatform_model_locations` | `aiplatform.googleapis.com/ModelLocation` | generic | +| aiplatform | `gcp_aiplatform_models` | `aiplatform.googleapis.com/Model` | generic | +| aiplatform | `gcp_aiplatform_operations` | `aiplatform.googleapis.com/Operation` | generic | +| aiplatform | `gcp_aiplatform_pipeline_jobs` | `aiplatform.googleapis.com/PipelineJob` | generic | +| aiplatform | `gcp_aiplatform_pipeline_locations` | `aiplatform.googleapis.com/PipelineLocation` | generic | +| aiplatform | `gcp_aiplatform_specialist_pools` | `aiplatform.googleapis.com/SpecialistPool` | generic | +| aiplatform | `gcp_aiplatform_specialistpool_locations` | `aiplatform.googleapis.com/SpecialistpoolLocation` | generic | +| aiplatform | `gcp_aiplatform_studies` | `aiplatform.googleapis.com/Study` | generic | +| aiplatform | `gcp_aiplatform_tensorboard_locations` | `aiplatform.googleapis.com/TensorboardLocation` | generic | +| aiplatform | `gcp_aiplatform_tensorboards` | `aiplatform.googleapis.com/Tensorboard` | generic | +| aiplatform | `gcp_aiplatform_training_pipelines` | `aiplatform.googleapis.com/TrainingPipeline` | generic | +| aiplatform | `gcp_aiplatform_vizier_locations` | `aiplatform.googleapis.com/VizierLocation` | generic | +| alloydb | `gcp_alloydb_clusters` | `alloydb.googleapis.com/Cluster` | generic | +| alloydb | `gcp_alloydb_instances` | `alloydb.googleapis.com/Instance` | generic | +| apigateway | `gcp_apigateway_apis` | `apigateway.googleapis.com/Api` | generic | +| apigateway | `gcp_apigateway_gateways` | `apigateway.googleapis.com/Gateway` | generic | +| apikeys | `gcp_apikeys_keys` | `apikeys.googleapis.com/Key` | generic | +| appengine | `gcp_appengine_apps` | `appengine.googleapis.com/Application` | generic | +| appengine | `gcp_appengine_authorized_certificates` | `appengine.googleapis.com/AuthorizedCertificate` | generic | +| appengine | `gcp_appengine_authorized_domains` | `appengine.googleapis.com/AuthorizedDomain` | generic | +| appengine | `gcp_appengine_domain_mappings` | `appengine.googleapis.com/DomainMapping` | generic | +| appengine | `gcp_appengine_firewall_ingress_rules` | `appengine.googleapis.com/FirewallIngressRule` | generic | +| appengine | `gcp_appengine_instances` | `appengine.googleapis.com/Instance` | generic | +| appengine | `gcp_appengine_services` | `appengine.googleapis.com/Service` | generic | +| appengine | `gcp_appengine_versions` | `appengine.googleapis.com/Version` | generic | +| applicationintegration | `gcp_applicationintegration_authconfigs` | `applicationintegration.googleapis.com/Authconfig` | generic | +| applicationintegration | `gcp_applicationintegration_certificates` | `applicationintegration.googleapis.com/Certificate` | generic | +| applicationintegration | `gcp_applicationintegration_integration_execution_suspensions` | `applicationintegration.googleapis.com/IntegrationExecutionSuspension` | generic | +| applicationintegration | `gcp_applicationintegration_integration_executions` | `applicationintegration.googleapis.com/IntegrationExecution` | generic | +| applicationintegration | `gcp_applicationintegration_integration_versions` | `applicationintegration.googleapis.com/IntegrationVersion` | generic | +| applicationintegration | `gcp_applicationintegration_integrations` | `applicationintegration.googleapis.com/Integration` | generic | +| applicationintegration | `gcp_applicationintegration_sfdc_channels` | `applicationintegration.googleapis.com/SfdcChannel` | generic | +| applicationintegration | `gcp_applicationintegration_sfdc_instances` | `applicationintegration.googleapis.com/SfdcInstance` | generic | +| artifactregistry | `gcp_artifactregistry_docker_images` | `artifactregistry.googleapis.com/DockerImage` | generic | +| artifactregistry | `gcp_artifactregistry_files` | `artifactregistry.googleapis.com/File` | generic | +| artifactregistry | `gcp_artifactregistry_locations` | `artifactregistry.googleapis.com/Location` | generic | +| artifactregistry | `gcp_artifactregistry_packages` | `artifactregistry.googleapis.com/Package` | generic | +| artifactregistry | `gcp_artifactregistry_repositories` | `artifactregistry.googleapis.com/Repository` | generic | +| artifactregistry | `gcp_artifactregistry_tags` | `artifactregistry.googleapis.com/Tag` | generic | +| artifactregistry | `gcp_artifactregistry_versions` | `artifactregistry.googleapis.com/Version` | generic | +| baremetalsolution | `gcp_baremetalsolution_instances` | `baremetalsolution.googleapis.com/Instance` | generic | +| baremetalsolution | `gcp_baremetalsolution_networks` | `baremetalsolution.googleapis.com/Network` | generic | +| baremetalsolution | `gcp_baremetalsolution_nfs_shares` | `baremetalsolution.googleapis.com/NfsShare` | generic | +| baremetalsolution | `gcp_baremetalsolution_volume_luns` | `baremetalsolution.googleapis.com/VolumeLun` | generic | +| baremetalsolution | `gcp_baremetalsolution_volumes` | `baremetalsolution.googleapis.com/Volume` | generic | +| batch | `gcp_batch_jobs` | `batch.googleapis.com/Job` | generic | +| batch | `gcp_batch_task_groups` | `batch.googleapis.com/TaskGroup` | generic | +| batch | `gcp_batch_tasks` | `batch.googleapis.com/Task` | generic | +| beyondcorp | `gcp_beyondcorp_app_connections` | `beyondcorp.googleapis.com/AppConnection` | generic | +| beyondcorp | `gcp_beyondcorp_app_connectors` | `beyondcorp.googleapis.com/AppConnector` | generic | +| beyondcorp | `gcp_beyondcorp_app_gateways` | `beyondcorp.googleapis.com/AppGateway` | generic | +| bigquery | `gcp_bigquery_datasets` | `bigquery.googleapis.com/Dataset` | generic | +| bigquery | `gcp_bigquery_tables` | `bigquery.googleapis.com/Table` | generic | +| bigquerydatatransfer | `gcp_bigquerydatatransfer_configs` | `bigquerydatatransfer.googleapis.com/Config` | generic | +| bigquerydatatransfer | `gcp_bigquerydatatransfer_datasources` | `bigquerydatatransfer.googleapis.com/Datasource` | generic | +| bigquerydatatransfer | `gcp_bigquerydatatransfer_locations` | `bigquerydatatransfer.googleapis.com/Location` | generic | +| bigquerydatatransfer | `gcp_bigquerydatatransfer_logs` | `bigquerydatatransfer.googleapis.com/Log` | generic | +| bigquerydatatransfer | `gcp_bigquerydatatransfer_runs` | `bigquerydatatransfer.googleapis.com/Run` | generic | +| bigtableadmin | `gcp_bigtableadmin_app_profiles` | `bigtableadmin.googleapis.com/AppProfile` | generic | +| bigtableadmin | `gcp_bigtableadmin_backups` | `bigtableadmin.googleapis.com/Backup` | generic | +| bigtableadmin | `gcp_bigtableadmin_clusters` | `bigtableadmin.googleapis.com/Cluster` | generic | +| bigtableadmin | `gcp_bigtableadmin_instances` | `bigtableadmin.googleapis.com/Instance` | generic | +| bigtableadmin | `gcp_bigtableadmin_tables` | `bigtableadmin.googleapis.com/Table` | generic | +| billing | `gcp_billing_billing_account_subaccounts` | `billing.googleapis.com/BillingAccountSubaccount` | generic | +| billing | `gcp_billing_billing_accounts` | `-` | generic | +| billing | `gcp_billing_budgets` | `billing.googleapis.com/Budget` | generic | +| billing | `gcp_billing_projects` | `billing.googleapis.com/Project` | generic | +| billing | `gcp_billing_service_skus` | `billing.googleapis.com/ServiceSku` | generic | +| billing | `gcp_billing_services` | `billing.googleapis.com/Service` | generic | +| binaryauthorization | `gcp_binaryauthorization_assertors` | `binaryauthorization.googleapis.com/Assertor` | generic | +| certificatemanager | `gcp_certificatemanager_certificate_issuance_configs` | `certificatemanager.googleapis.com/CertificateIssuanceConfig` | generic | +| certificatemanager | `gcp_certificatemanager_certificate_map_entries` | `certificatemanager.googleapis.com/CertificateMapEntry` | generic | +| certificatemanager | `gcp_certificatemanager_certificate_maps` | `certificatemanager.googleapis.com/CertificateMap` | generic | +| certificatemanager | `gcp_certificatemanager_certificates` | `certificatemanager.googleapis.com/Certificate` | generic | +| certificatemanager | `gcp_certificatemanager_dns_authorizations` | `certificatemanager.googleapis.com/DnsAuthorization` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets` | `cloudasset.googleapis.com/Asset` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_access_policies` | `cloudasset.googleapis.com/AssetsAccessPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_history` | `cloudasset.googleapis.com/AssetsHistory` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_iam_policies` | `cloudasset.googleapis.com/AssetsIamPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_org_policies` | `cloudasset.googleapis.com/AssetsOrgPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_os_inventories` | `cloudasset.googleapis.com/AssetsOsInventory` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_relationships` | `cloudasset.googleapis.com/AssetsRelationship` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_assets_resources` | `cloudasset.googleapis.com/AssetsResource` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_effective_iam_policies` | `cloudasset.googleapis.com/EffectiveIamPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_feeds` | `cloudasset.googleapis.com/Feed` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets` | `cloudasset.googleapis.com/OrgAsset` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets_access_policies` | `cloudasset.googleapis.com/OrgAssetsAccessPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets_iam_policies` | `cloudasset.googleapis.com/OrgAssetsIamPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets_org_policies` | `cloudasset.googleapis.com/OrgAssetsOrgPolicy` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets_os_inventories` | `cloudasset.googleapis.com/OrgAssetsOsInventory` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets_relationships` | `cloudasset.googleapis.com/OrgAssetsRelationship` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_org_assets_resources` | `cloudasset.googleapis.com/OrgAssetsResource` | generic | +| cloudassetinventory | `gcp_cloudassetinventory_savedqueries` | `cloudasset.googleapis.com/Savedquery` | generic | +| cloudbuild | `gcp_cloudbuild_builds` | `cloudbuild.googleapis.com/Build` | generic | +| cloudbuild | `gcp_cloudbuild_connection_repositories` | `cloudbuild.googleapis.com/ConnectionRepository` | generic | +| cloudbuild | `gcp_cloudbuild_connections` | `cloudbuild.googleapis.com/Connection` | generic | +| cloudbuild | `gcp_cloudbuild_triggers` | `cloudbuild.googleapis.com/Trigger` | generic | +| cloudbuild | `gcp_cloudbuild_worker_pools` | `cloudbuild.googleapis.com/WorkerPool` | generic | +| clouddeploy | `gcp_clouddeploy_delivery_pipelines` | `clouddeploy.googleapis.com/DeliveryPipeline` | generic | +| clouddeploy | `gcp_clouddeploy_job_runs` | `clouddeploy.googleapis.com/JobRun` | generic | +| clouddeploy | `gcp_clouddeploy_releases` | `clouddeploy.googleapis.com/Releas` | generic | +| clouddeploy | `gcp_clouddeploy_rollouts` | `clouddeploy.googleapis.com/Rollout` | generic | +| clouddeploy | `gcp_clouddeploy_targets` | `clouddeploy.googleapis.com/Target` | generic | +| clouderrorreporting | `gcp_clouderrorreporting_error_events` | `clouderrorreporting.googleapis.com/ErrorEvent` | generic | +| clouderrorreporting | `gcp_clouderrorreporting_error_group_stats` | `clouderrorreporting.googleapis.com/ErrorGroupStat` | generic | +| cloudresourcemanager | `gcp_cloudresourcemanager_organizations` | `cloudresourcemanager.googleapis.com/Organization` | generic | +| cloudscheduler | `gcp_cloudscheduler_jobs` | `cloudscheduler.googleapis.com/Job` | generic | +| cloudscheduler | `gcp_cloudscheduler_locations` | `cloudscheduler.googleapis.com/Location` | generic | +| cloudsupport | `gcp_cloudsupport_cases` | `cloudsupport.googleapis.com/Cas` | generic | +| cloudtasks | `gcp_cloudtasks_locations` | `cloudtasks.googleapis.com/Location` | generic | +| cloudtasks | `gcp_cloudtasks_queues` | `cloudtasks.googleapis.com/Queue` | generic | +| cloudtasks | `gcp_cloudtasks_tasks` | `cloudtasks.googleapis.com/Task` | generic | +| cloudtrace | `gcp_cloudtrace_traces` | `cloudtrace.googleapis.com/Trace` | generic | +| composer | `gcp_composer_environments` | `composer.googleapis.com/Environment` | generic | +| composer | `gcp_composer_image_versions` | `composer.googleapis.com/ImageVersion` | generic | +| composer | `gcp_composer_operations` | `composer.googleapis.com/Operation` | generic | +| compute | `gcp_compute_addresses` | `compute.googleapis.com/Address` | typed | +| compute | `gcp_compute_autoscalers` | `compute.googleapis.com/Autoscaler` | generic | +| compute | `gcp_compute_backend_buckets` | `compute.googleapis.com/BackendBucket` | generic | +| compute | `gcp_compute_backend_services` | `compute.googleapis.com/BackendService` | generic | +| compute | `gcp_compute_disk_types` | `compute.googleapis.com/DiskType` | generic | +| compute | `gcp_compute_disks` | `compute.googleapis.com/Disk` | typed | +| compute | `gcp_compute_external_vpn_gateways` | `compute.googleapis.com/ExternalVpnGateway` | generic | +| compute | `gcp_compute_firewalls` | `compute.googleapis.com/Firewall` | typed | +| compute | `gcp_compute_forwarding_rules` | `compute.googleapis.com/ForwardingRule` | generic | +| compute | `gcp_compute_global_addresses` | `compute.googleapis.com/GlobalAddress` | generic | +| compute | `gcp_compute_global_forwarding_rules` | `compute.googleapis.com/GlobalForwardingRule` | generic | +| compute | `gcp_compute_health_checks` | `compute.googleapis.com/HealthCheck` | generic | +| compute | `gcp_compute_image_policies` | `compute.googleapis.com/ImagePolicy` | generic | +| compute | `gcp_compute_images` | `compute.googleapis.com/Image` | generic | +| compute | `gcp_compute_instance_group_instances` | `compute.googleapis.com/InstanceGroupInstance` | generic | +| compute | `gcp_compute_instance_group_managers` | `compute.googleapis.com/InstanceGroupManager` | generic | +| compute | `gcp_compute_instance_group_regional_instances` | `compute.googleapis.com/InstanceGroupRegionalInstance` | generic | +| compute | `gcp_compute_instance_groups` | `compute.googleapis.com/InstanceGroup` | generic | +| compute | `gcp_compute_instance_tag_bindings` | `compute.googleapis.com/InstanceTagBinding` | generic | +| compute | `gcp_compute_instance_templates` | `compute.googleapis.com/InstanceTemplate` | generic | +| compute | `gcp_compute_instances` | `compute.googleapis.com/Instance` | typed | +| compute | `gcp_compute_interconnect_attachments` | `compute.googleapis.com/InterconnectAttachment` | generic | +| compute | `gcp_compute_interconnect_locations` | `compute.googleapis.com/InterconnectLocation` | generic | +| compute | `gcp_compute_interconnect_remote_locations` | `compute.googleapis.com/InterconnectRemoteLocation` | generic | +| compute | `gcp_compute_interconnects` | `compute.googleapis.com/Interconnect` | generic | +| compute | `gcp_compute_machine_types` | `compute.googleapis.com/MachineType` | generic | +| compute | `gcp_compute_network_endpoint_groups` | `compute.googleapis.com/NetworkEndpointGroup` | generic | +| compute | `gcp_compute_network_firewall_policies` | `compute.googleapis.com/NetworkFirewallPolicy` | generic | +| compute | `gcp_compute_networks` | `compute.googleapis.com/Network` | typed | +| compute | `gcp_compute_osconfig_inventories` | `compute.googleapis.com/OsconfigInventory` | generic | +| compute | `gcp_compute_osconfig_os_patch_deployments` | `compute.googleapis.com/OsconfigOsPatchDeployment` | generic | +| compute | `gcp_compute_osconfig_os_patch_jobs` | `compute.googleapis.com/OsconfigOsPatchJob` | generic | +| compute | `gcp_compute_osconfig_os_patch_jobs_instance_details` | `compute.googleapis.com/OsconfigOsPatchJobsInstanceDetail` | generic | +| compute | `gcp_compute_osconfig_os_policy_assignment_reports` | `compute.googleapis.com/OsconfigOsPolicyAssignmentReport` | generic | +| compute | `gcp_compute_osconfig_os_policy_assignments` | `compute.googleapis.com/OsconfigOsPolicyAssignment` | generic | +| compute | `gcp_compute_osconfig_os_vulnerability_reports` | `compute.googleapis.com/OsconfigOsVulnerabilityReport` | generic | +| compute | `gcp_compute_packet_mirrorings` | `compute.googleapis.com/PacketMirroring` | generic | +| compute | `gcp_compute_projects` | `compute.googleapis.com/Project` | generic | +| compute | `gcp_compute_region_instance_templates` | `compute.googleapis.com/RegionInstanceTemplate` | generic | +| compute | `gcp_compute_region_network_firewall_policies` | `compute.googleapis.com/RegionNetworkFirewallPolicy` | generic | +| compute | `gcp_compute_router_nat_mapping_infos` | `compute.googleapis.com/RouterNatMappingInfo` | generic | +| compute | `gcp_compute_routers` | `compute.googleapis.com/Router` | generic | +| compute | `gcp_compute_routes` | `compute.googleapis.com/Route` | generic | +| compute | `gcp_compute_security_policies` | `compute.googleapis.com/SecurityPolicy` | generic | +| compute | `gcp_compute_snapshots` | `compute.googleapis.com/Snapshot` | typed | +| compute | `gcp_compute_ssl_certificates` | `compute.googleapis.com/SslCertificate` | generic | +| compute | `gcp_compute_ssl_policies` | `compute.googleapis.com/SslPolicy` | generic | +| compute | `gcp_compute_subnetworks` | `compute.googleapis.com/Subnetwork` | typed | +| compute | `gcp_compute_target_grpc_proxies` | `compute.googleapis.com/TargetGrpcProxy` | generic | +| compute | `gcp_compute_target_http_proxies` | `compute.googleapis.com/TargetHttpProxy` | generic | +| compute | `gcp_compute_target_https_proxies` | `compute.googleapis.com/TargetHttpsProxy` | generic | +| compute | `gcp_compute_target_instances` | `compute.googleapis.com/TargetInstance` | generic | +| compute | `gcp_compute_target_pools` | `compute.googleapis.com/TargetPool` | generic | +| compute | `gcp_compute_target_ssl_proxies` | `compute.googleapis.com/TargetSslProxy` | generic | +| compute | `gcp_compute_target_tcp_proxies` | `compute.googleapis.com/TargetTcpProxy` | generic | +| compute | `gcp_compute_target_vpn_gateways` | `compute.googleapis.com/TargetVpnGateway` | generic | +| compute | `gcp_compute_url_maps` | `compute.googleapis.com/UrlMap` | generic | +| compute | `gcp_compute_vpn_gateways` | `compute.googleapis.com/VpnGateway` | generic | +| compute | `gcp_compute_vpn_tunnels` | `compute.googleapis.com/VpnTunnel` | generic | +| compute | `gcp_compute_zones` | `compute.googleapis.com/Zone` | generic | +| container | `gcp_container_clusters` | `container.googleapis.com/Cluster` | typed | +| container | `gcp_container_node_pools` | `container.googleapis.com/NodePool` | generic | +| containeranalysis | `gcp_containeranalysis_occurrences` | `containeranalysis.googleapis.com/Occurrence` | generic | +| databasemigration | `gcp_databasemigration_locations` | `datamigration.googleapis.com/Location` | generic | +| databasemigration | `gcp_databasemigration_migration_jobs` | `datamigration.googleapis.com/MigrationJob` | generic | +| databasemigration | `gcp_databasemigration_operations` | `datamigration.googleapis.com/Operation` | generic | +| dataflow | `gcp_dataflow_job_messages` | `dataflow.googleapis.com/JobMessage` | generic | +| dataflow | `gcp_dataflow_job_metrics` | `dataflow.googleapis.com/JobMetric` | generic | +| dataflow | `gcp_dataflow_jobs` | `dataflow.googleapis.com/Job` | generic | +| dataflow | `gcp_dataflow_snapshots` | `dataflow.googleapis.com/Snapshot` | generic | +| datafusion | `gcp_datafusion_available_versions` | `datafusion.googleapis.com/AvailableVersion` | generic | +| datafusion | `gcp_datafusion_instance_operations` | `datafusion.googleapis.com/InstanceOperation` | generic | +| datafusion | `gcp_datafusion_instances` | `datafusion.googleapis.com/Instance` | generic | +| dataproc | `gcp_dataproc_autoscaling_policies` | `dataproc.googleapis.com/AutoscalingPolicy` | generic | +| dataproc | `gcp_dataproc_cluster_nodegroups` | `dataproc.googleapis.com/ClusterNodegroup` | generic | +| dataproc | `gcp_dataproc_clusters` | `dataproc.googleapis.com/Cluster` | generic | +| dataproc | `gcp_dataproc_jobs` | `dataproc.googleapis.com/Job` | generic | +| dataproc | `gcp_dataproc_regions` | `dataproc.googleapis.com/Region` | generic | +| deploymentmanager | `gcp_deploymentmanager_deployments` | `deploymentmanager.googleapis.com/Deployment` | generic | +| deploymentmanager | `gcp_deploymentmanager_manifests` | `deploymentmanager.googleapis.com/Manifest` | generic | +| deploymentmanager | `gcp_deploymentmanager_operations` | `deploymentmanager.googleapis.com/Operation` | generic | +| deploymentmanager | `gcp_deploymentmanager_resources` | `deploymentmanager.googleapis.com/Resource` | generic | +| deploymentmanager | `gcp_deploymentmanager_types` | `deploymentmanager.googleapis.com/Type` | generic | +| dns | `gcp_dns_managed_zones` | `dns.googleapis.com/ManagedZone` | generic | +| dns | `gcp_dns_policies` | `dns.googleapis.com/Policy` | generic | +| dns | `gcp_dns_resource_record_sets` | `dns.googleapis.com/ResourceRecordSet` | generic | +| domains | `gcp_domains_registrations` | `domains.googleapis.com/Registration` | generic | +| essentialcontacts | `gcp_essentialcontacts_folder_contacts` | `essentialcontacts.googleapis.com/FolderContact` | generic | +| essentialcontacts | `gcp_essentialcontacts_organization_contacts` | `essentialcontacts.googleapis.com/OrganizationContact` | generic | +| essentialcontacts | `gcp_essentialcontacts_project_contacts` | `essentialcontacts.googleapis.com/ProjectContact` | generic | +| eventarc | `gcp_eventarc_channels` | `eventarc.googleapis.com/Channel` | generic | +| eventarc | `gcp_eventarc_providers` | `eventarc.googleapis.com/Provider` | generic | +| eventarc | `gcp_eventarc_triggers` | `eventarc.googleapis.com/Trigger` | generic | +| filestore | `gcp_filestore_backups` | `file.googleapis.com/Backup` | generic | +| filestore | `gcp_filestore_instances` | `file.googleapis.com/Instance` | generic | +| filestore | `gcp_filestore_locations` | `file.googleapis.com/Location` | generic | +| filestore | `gcp_filestore_operations` | `file.googleapis.com/Operation` | generic | +| filestore | `gcp_filestore_snapshots` | `file.googleapis.com/Snapshot` | generic | +| firebase | `gcp_firebase_hosting_site_channels` | `firebase.googleapis.com/HostingSiteChannel` | generic | +| firebase | `gcp_firebase_hosting_site_custom_domains` | `firebase.googleapis.com/HostingSiteCustomDomain` | generic | +| firebase | `gcp_firebase_hosting_site_releases` | `firebase.googleapis.com/HostingSiteReleas` | generic | +| firebase | `gcp_firebase_hosting_site_versions` | `firebase.googleapis.com/HostingSiteVersion` | generic | +| firebase | `gcp_firebase_hosting_sites` | `firebase.googleapis.com/HostingSite` | generic | +| firebase | `gcp_firebase_rules_releases` | `firebase.googleapis.com/RulesReleas` | generic | +| firebase | `gcp_firebase_rules_rulesets` | `firebase.googleapis.com/RulesRuleset` | generic | +| firebaseappcheck | `gcp_firebaseappcheck_app_attest_configs` | `firebaseappcheck.googleapis.com/AppAttestConfig` | generic | +| firebaseappcheck | `gcp_firebaseappcheck_device_check_configs` | `firebaseappcheck.googleapis.com/DeviceCheckConfig` | generic | +| firebaseappcheck | `gcp_firebaseappcheck_play_integrity_configs` | `firebaseappcheck.googleapis.com/PlayIntegrityConfig` | generic | +| firebaseappcheck | `gcp_firebaseappcheck_recaptcha_configs` | `firebaseappcheck.googleapis.com/RecaptchaConfig` | generic | +| firebaseappcheck | `gcp_firebaseappcheck_recaptcha_enterprise_configs` | `firebaseappcheck.googleapis.com/RecaptchaEnterpriseConfig` | generic | +| firebaseappcheck | `gcp_firebaseappcheck_safety_net_configs` | `firebaseappcheck.googleapis.com/SafetyNetConfig` | generic | +| firestore | `gcp_firestore_databases` | `firestore.googleapis.com/Databas` | generic | +| functions | `gcp_functions_function_policies` | `cloudfunctions.googleapis.com/FunctionPolicy` | generic | +| functions | `gcp_functions_functions` | `cloudfunctions.googleapis.com/CloudFunction` | generic | +| functionsv2 | `gcp_functionsv2_function_policies` | `cloudfunctions.googleapis.com/FunctionPolicy` | generic | +| functionsv2 | `gcp_functionsv2_functions` | `cloudfunctions.googleapis.com/Function` | generic | +| iam | `gcp_iam_deny_policies` | `iam.googleapis.com/DenyPolicy` | generic | +| iam | `gcp_iam_organizational_roles` | `iam.googleapis.com/OrganizationalRole` | generic | +| iam | `gcp_iam_predefined_roles` | `iam.googleapis.com/PredefinedRole` | generic | +| iam | `gcp_iam_roles` | `iam.googleapis.com/Role` | generic | +| iam | `gcp_iam_service_account_keys` | `iam.googleapis.com/ServiceAccountKey` | generic | +| iam | `gcp_iam_service_account_policies` | `iam.googleapis.com/ServiceAccountPolicy` | generic | +| iam | `gcp_iam_service_accounts` | `iam.googleapis.com/ServiceAccount` | typed | +| iam | `gcp_iam_workload_identity_pool_providers` | `iam.googleapis.com/WorkloadIdentityPoolProvider` | generic | +| iam | `gcp_iam_workload_identity_pools` | `iam.googleapis.com/WorkloadIdentityPool` | generic | +| identitytoolkit | `gcp_identitytoolkit_accounts` | `identitytoolkit.googleapis.com/Account` | generic | +| identitytoolkit | `gcp_identitytoolkit_default_supported_idps` | `identitytoolkit.googleapis.com/DefaultSupportedIdp` | generic | +| identitytoolkit | `gcp_identitytoolkit_inbound_saml_configs` | `identitytoolkit.googleapis.com/InboundSamlConfig` | generic | +| identitytoolkit | `gcp_identitytoolkit_oauth_idp_configs` | `identitytoolkit.googleapis.com/OauthIdpConfig` | generic | +| identitytoolkit | `gcp_identitytoolkit_project_configs` | `identitytoolkit.googleapis.com/ProjectConfig` | generic | +| identitytoolkit | `gcp_identitytoolkit_project_public_configs` | `identitytoolkit.googleapis.com/ProjectPublicConfig` | generic | +| identitytoolkit | `gcp_identitytoolkit_recaptcha_params` | `identitytoolkit.googleapis.com/RecaptchaParam` | generic | +| identitytoolkit | `gcp_identitytoolkit_tenants` | `identitytoolkit.googleapis.com/Tenant` | generic | +| kms | `gcp_kms_crypto_key_versions` | `cloudkms.googleapis.com/CryptoKeyVersion` | generic | +| kms | `gcp_kms_crypto_keys` | `cloudkms.googleapis.com/CryptoKey` | generic | +| kms | `gcp_kms_ekm_connections` | `cloudkms.googleapis.com/EkmConnection` | generic | +| kms | `gcp_kms_import_jobs` | `cloudkms.googleapis.com/ImportJob` | generic | +| kms | `gcp_kms_keyrings` | `cloudkms.googleapis.com/KeyRing` | generic | +| kms | `gcp_kms_locations` | `cloudkms.googleapis.com/Location` | generic | +| livestream | `gcp_livestream_channels` | `livestream.googleapis.com/Channel` | generic | +| livestream | `gcp_livestream_inputs` | `livestream.googleapis.com/Input` | generic | +| logging | `gcp_logging_audit_logs` | `logging.googleapis.com/AuditLog` | generic | +| logging | `gcp_logging_metrics` | `logging.googleapis.com/Metric` | generic | +| logging | `gcp_logging_sinks` | `logging.googleapis.com/Sink` | generic | +| looker | `gcp_looker_instance_operations` | `looker.googleapis.com/InstanceOperation` | generic | +| looker | `gcp_looker_instances` | `looker.googleapis.com/Instance` | generic | +| memorystore | `gcp_memorystore_instances` | `memorystore.googleapis.com/Instance` | generic | +| memorystore | `gcp_memorystore_locations` | `memorystore.googleapis.com/Location` | generic | +| memorystore | `gcp_memorystore_operations` | `memorystore.googleapis.com/Operation` | generic | +| monitoring | `gcp_monitoring_alert_policies` | `monitoring.googleapis.com/AlertPolicy` | generic | +| networkconnectivity | `gcp_networkconnectivity_internal_ranges` | `networkconnectivity.googleapis.com/InternalRange` | generic | +| networkconnectivity | `gcp_networkconnectivity_locations` | `networkconnectivity.googleapis.com/Location` | generic | +| networkintelligencecenter | `gcp_networkintelligencecenter_connectivity_tests` | `networkmanagement.googleapis.com/ConnectivityTest` | generic | +| networkintelligencecenter | `gcp_networkintelligencecenter_locations` | `networkmanagement.googleapis.com/Location` | generic | +| networkintelligencecenter | `gcp_networkintelligencecenter_operations` | `networkmanagement.googleapis.com/Operation` | generic | +| networksecurity | `gcp_networksecurity_address_groups` | `networksecurity.googleapis.com/AddressGroup` | generic | +| networksecurity | `gcp_networksecurity_firewall_endpoints` | `networksecurity.googleapis.com/FirewallEndpoint` | generic | +| networksecurity | `gcp_networksecurity_security_profile_groups` | `networksecurity.googleapis.com/SecurityProfileGroup` | generic | +| networksecurity | `gcp_networksecurity_security_profiles` | `networksecurity.googleapis.com/SecurityProfile` | generic | +| networksecurity | `gcp_networksecurity_tls_inspection_policies` | `networksecurity.googleapis.com/TlsInspectionPolicy` | generic | +| networkservices | `gcp_networkservices_endpoint_policies` | `networkservices.googleapis.com/EndpointPolicy` | generic | +| networkservices | `gcp_networkservices_gateways` | `networkservices.googleapis.com/Gateway` | generic | +| networkservices | `gcp_networkservices_grpc_routes` | `networkservices.googleapis.com/GrpcRoute` | generic | +| networkservices | `gcp_networkservices_http_routes` | `networkservices.googleapis.com/HttpRoute` | generic | +| networkservices | `gcp_networkservices_meshes` | `networkservices.googleapis.com/Mesh` | generic | +| networkservices | `gcp_networkservices_service_bindings` | `networkservices.googleapis.com/ServiceBinding` | generic | +| networkservices | `gcp_networkservices_tcp_routes` | `networkservices.googleapis.com/TcpRoute` | generic | +| networkservices | `gcp_networkservices_tls_routes` | `networkservices.googleapis.com/TlsRoute` | generic | +| organization | `gcp_organization_folders_policies` | `cloudresourcemanager.googleapis.com/FoldersPolicy` | generic | +| organization | `gcp_organization_policies` | `cloudresourcemanager.googleapis.com/Policy` | generic | +| organization | `gcp_organization_projects_policies` | `cloudresourcemanager.googleapis.com/ProjectsPolicy` | generic | +| policyanalyzer | `gcp_policyanalyzer_activities` | `policyanalyzer.googleapis.com/Activity` | generic | +| privateca | `gcp_privateca_authorities` | `privateca.googleapis.com/Authority` | generic | +| privateca | `gcp_privateca_certificates` | `privateca.googleapis.com/Certificate` | generic | +| privateca | `gcp_privateca_pools` | `privateca.googleapis.com/Pool` | generic | +| projects | `gcp_projects` | `cloudresourcemanager.googleapis.com/Project` | typed | +| pubsub | `gcp_pubsub_schema_revisions` | `pubsub.googleapis.com/SchemaRevision` | generic | +| pubsub | `gcp_pubsub_schemas` | `pubsub.googleapis.com/Schema` | generic | +| pubsub | `gcp_pubsub_snapshots` | `pubsub.googleapis.com/Snapshot` | generic | +| pubsub | `gcp_pubsub_subscriptions` | `pubsub.googleapis.com/Subscription` | typed | +| pubsub | `gcp_pubsub_topics` | `pubsub.googleapis.com/Topic` | typed | +| recommendations | `gcp_recommendations_folders` | `recommendations.googleapis.com/Folder` | generic | +| recommendations | `gcp_recommendations_folders_insights` | `recommendations.googleapis.com/FoldersInsight` | generic | +| recommendations | `gcp_recommendations_folders_locations` | `recommendations.googleapis.com/FoldersLocation` | generic | +| recommendations | `gcp_recommendations_organizations` | `recommendations.googleapis.com/Organization` | generic | +| recommendations | `gcp_recommendations_organizations_insights` | `recommendations.googleapis.com/OrganizationsInsight` | generic | +| recommendations | `gcp_recommendations_organizations_locations` | `recommendations.googleapis.com/OrganizationsLocation` | generic | +| recommendations | `gcp_recommendations_projects` | `recommendations.googleapis.com/Project` | generic | +| recommendations | `gcp_recommendations_projects_insights` | `recommendations.googleapis.com/ProjectsInsight` | generic | +| recommendations | `gcp_recommendations_projects_locations` | `recommendations.googleapis.com/ProjectsLocation` | generic | +| redis | `gcp_redis_instances` | `redis.googleapis.com/Instance` | generic | +| resourcemanager | `gcp_resourcemanager_folder_policies` | `cloudresourcemanager.googleapis.com/FolderPolicy` | generic | +| resourcemanager | `gcp_resourcemanager_folders` | `cloudresourcemanager.googleapis.com/Folder` | generic | +| resourcemanager | `gcp_resourcemanager_organization_policies` | `cloudresourcemanager.googleapis.com/OrganizationPolicy` | generic | +| resourcemanager | `gcp_resourcemanager_organization_projects` | `cloudresourcemanager.googleapis.com/OrganizationProject` | generic | +| resourcemanager | `gcp_resourcemanager_organization_tag_keys` | `cloudresourcemanager.googleapis.com/OrganizationTagKey` | generic | +| resourcemanager | `gcp_resourcemanager_organization_tag_values` | `cloudresourcemanager.googleapis.com/OrganizationTagValue` | generic | +| resourcemanager | `gcp_resourcemanager_project_policies` | `cloudresourcemanager.googleapis.com/ProjectPolicy` | generic | +| resourcemanager | `gcp_resourcemanager_project_tag_bindings` | `cloudresourcemanager.googleapis.com/ProjectTagBinding` | generic | +| resourcemanager | `gcp_resourcemanager_project_tag_keys` | `cloudresourcemanager.googleapis.com/ProjectTagKey` | generic | +| resourcemanager | `gcp_resourcemanager_project_tag_values` | `cloudresourcemanager.googleapis.com/ProjectTagValue` | generic | +| resourcemanager | `gcp_resourcemanager_projects` | `cloudresourcemanager.googleapis.com/Project` | generic | +| resourcemanager | `gcp_resourcemanager_projects_search` | `cloudresourcemanager.googleapis.com/ProjectsSearch` | generic | +| resourcemanager | `gcp_resourcemanager_subfolders` | `cloudresourcemanager.googleapis.com/Subfolder` | generic | +| run | `gcp_run_executions` | `run.googleapis.com/Execution` | generic | +| run | `gcp_run_job_policies` | `run.googleapis.com/JobPolicy` | generic | +| run | `gcp_run_jobs` | `run.googleapis.com/Job` | generic | +| run | `gcp_run_locations` | `run.googleapis.com/Location` | generic | +| run | `gcp_run_revisions` | `run.googleapis.com/Revision` | generic | +| run | `gcp_run_service_policies` | `run.googleapis.com/ServicePolicy` | generic | +| run | `gcp_run_services` | `run.googleapis.com/Service` | generic | +| run | `gcp_run_tasks` | `run.googleapis.com/Task` | generic | +| run | `gcp_run_worker_pool_policies` | `run.googleapis.com/WorkerPoolPolicy` | generic | +| run | `gcp_run_worker_pools` | `run.googleapis.com/WorkerPool` | generic | +| secretmanager | `gcp_secretmanager_secrets` | `secretmanager.googleapis.com/Secret` | generic | +| securitycenter | `gcp_securitycenter_folder_event_threat_detection` | `securitycenter.googleapis.com/FolderEventThreatDetection` | generic | +| securitycenter | `gcp_securitycenter_folder_findings` | `securitycenter.googleapis.com/FolderFinding` | generic | +| securitycenter | `gcp_securitycenter_org_event_threat_detection_settings` | `securitycenter.googleapis.com/OrgEventThreatDetectionSetting` | generic | +| securitycenter | `gcp_securitycenter_organization_findings` | `securitycenter.googleapis.com/OrganizationFinding` | generic | +| securitycenter | `gcp_securitycenter_project_event_threat_detection` | `securitycenter.googleapis.com/ProjectEventThreatDetection` | generic | +| securitycenter | `gcp_securitycenter_project_findings` | `securitycenter.googleapis.com/ProjectFinding` | generic | +| servicehealth | `gcp_servicehealth_events` | `servicehealth.googleapis.com/Event` | generic | +| servicehealth | `gcp_servicehealth_locations` | `servicehealth.googleapis.com/Location` | generic | +| serviceusage | `gcp_serviceusage_service_project_quota_metrics` | `serviceusage.googleapis.com/ServiceProjectQuotaMetric` | generic | +| serviceusage | `gcp_serviceusage_services` | `serviceusage.googleapis.com/Service` | generic | +| sourcerepo | `gcp_sourcerepo_config` | `sourcerepo.googleapis.com/Config` | generic | +| sourcerepo | `gcp_sourcerepo_repos` | `sourcerepo.googleapis.com/Repo` | generic | +| spanner | `gcp_spanner_databases` | `spanner.googleapis.com/Database` | generic | +| spanner | `gcp_spanner_instances` | `spanner.googleapis.com/Instance` | generic | +| sql | `gcp_sql_backups` | `sqladmin.googleapis.com/Backup` | generic | +| sql | `gcp_sql_databases` | `sqladmin.googleapis.com/Databas` | generic | +| sql | `gcp_sql_instances` | `sqladmin.googleapis.com/Instance` | generic | +| sql | `gcp_sql_ssl_certs` | `sqladmin.googleapis.com/SslCert` | generic | +| sql | `gcp_sql_users` | `sqladmin.googleapis.com/User` | generic | +| storage | `gcp_storage_bucket_objects` | `storage.googleapis.com/BucketObject` | generic | +| storage | `gcp_storage_bucket_policies` | `storage.googleapis.com/BucketPolicy` | generic | +| storage | `gcp_storage_bucket_tag_bindings` | `storage.googleapis.com/BucketTagBinding` | generic | +| storage | `gcp_storage_buckets` | `storage.googleapis.com/Bucket` | typed | +| storage | `gcp_storage_hmac_keys` | `storage.googleapis.com/HmacKey` | generic | +| storagetransfer | `gcp_storagetransfer_agent_pools` | `storagetransfer.googleapis.com/AgentPool` | generic | +| storagetransfer | `gcp_storagetransfer_transfer_jobs` | `storagetransfer.googleapis.com/TransferJob` | generic | +| storagetransfer | `gcp_storagetransfer_transfer_operations` | `storagetransfer.googleapis.com/TransferOperation` | generic | +| translate | `gcp_translate_glossaries` | `translate.googleapis.com/Glossary` | generic | +| videotranscoder | `gcp_videotranscoder_job_templates` | `transcoder.googleapis.com/JobTemplate` | generic | +| videotranscoder | `gcp_videotranscoder_jobs` | `transcoder.googleapis.com/Job` | generic | +| vision | `gcp_vision_product_reference_images` | `vision.googleapis.com/ProductReferenceImage` | generic | +| vision | `gcp_vision_products` | `vision.googleapis.com/Product` | generic | +| vmmigration | `gcp_vmmigration_groups` | `vmmigration.googleapis.com/Group` | generic | +| vmmigration | `gcp_vmmigration_source_datacenter_connectors` | `vmmigration.googleapis.com/SourceDatacenterConnector` | generic | +| vmmigration | `gcp_vmmigration_source_migrating_vm_clone_jobs` | `vmmigration.googleapis.com/SourceMigratingVmCloneJob` | generic | +| vmmigration | `gcp_vmmigration_source_migrating_vm_cutover_jobs` | `vmmigration.googleapis.com/SourceMigratingVmCutoverJob` | generic | +| vmmigration | `gcp_vmmigration_source_migrating_vms` | `vmmigration.googleapis.com/SourceMigratingVm` | generic | +| vmmigration | `gcp_vmmigration_source_utilization_reports` | `vmmigration.googleapis.com/SourceUtilizationReport` | generic | +| vmmigration | `gcp_vmmigration_sources` | `vmmigration.googleapis.com/Source` | generic | +| vmmigration | `gcp_vmmigration_target_projects` | `vmmigration.googleapis.com/TargetProject` | generic | +| vpcaccess | `gcp_vpcaccess_connectors` | `vpcaccess.googleapis.com/Connector` | generic | +| vpcaccess | `gcp_vpcaccess_locations` | `vpcaccess.googleapis.com/Location` | generic | +| websecurityscanner | `gcp_websecurityscanner_scan_config_scan_run_crawled_urls` | `websecurityscanner.googleapis.com/ScanConfigScanRunCrawledUrl` | generic | +| websecurityscanner | `gcp_websecurityscanner_scan_config_scan_run_findings` | `websecurityscanner.googleapis.com/ScanConfigScanRunFinding` | generic | +| websecurityscanner | `gcp_websecurityscanner_scan_config_scan_runs` | `websecurityscanner.googleapis.com/ScanConfigScanRun` | generic | +| websecurityscanner | `gcp_websecurityscanner_scan_configs` | `websecurityscanner.googleapis.com/ScanConfig` | generic | +| workflows | `gcp_workflows_workflows` | `workflows.googleapis.com/Workflow` | generic | + diff --git a/docs/authoring/indexed-resources/kubernetes-resource-catalog.md b/docs/authoring/indexed-resources/kubernetes-resource-catalog.md new file mode 100644 index 00000000..cac60f51 --- /dev/null +++ b/docs/authoring/indexed-resources/kubernetes-resource-catalog.md @@ -0,0 +1,23 @@ +# Kubernetes resource catalog + +Built-in Kubernetes resource types the kubeapi indexer discovers. + +_Generated 2026-07-02 from `indexers/kubetypes.py`._ + +Custom CRDs are declared in generation rules (`resourceTypes`) and indexed +selectively — they are not listed here. + +| Resource type | Notes | +|---|---| +| `cluster` | Built-in Kubernetes kind | +| `namespace` | Built-in Kubernetes kind | +| `deployment` | Built-in Kubernetes kind | +| `daemonset` | Built-in Kubernetes kind | +| `statefulset` | Built-in Kubernetes kind | +| `job` | Built-in Kubernetes kind | +| `cronjob` | Built-in Kubernetes kind | +| `ingress` | Built-in Kubernetes kind | +| `service` | Built-in Kubernetes kind | +| `pod` | Built-in Kubernetes kind | +| `custom` | Built-in Kubernetes kind | +| `persistentvolumeclaim` | Built-in Kubernetes kind | diff --git a/docs/authoring/indexed-resources/runwhen-platform-resource-catalog.md b/docs/authoring/indexed-resources/runwhen-platform-resource-catalog.md new file mode 100644 index 00000000..db912bc3 --- /dev/null +++ b/docs/authoring/indexed-resources/runwhen-platform-resource-catalog.md @@ -0,0 +1,19 @@ +# RunWhen platform resource catalog + +Resource types indexed under `platform: runwhen`. + +_Generated 2026-07-02 from `scripts/runwhen/dump_runwhen_resource_catalog.py`._ + +| Resource type | Match names | Typed collector | Notes | +|---|---|---|---| +| `workspace` | `workspace` | yes | One instance per workspace-builder run | + +## match_resource properties (workspace) + +| Property | Description | +|---|---| +| `name` | Workspace name | +| `qualified_name` | Same as workspace name | +| `owner_email` | From workspaceInfo `workspaceOwnerEmail` | +| `location_id` | Default location id | +| `location_name` | Default location name | diff --git a/docs/authoring/indexed-resources/runwhen-platform.md b/docs/authoring/indexed-resources/runwhen-platform.md new file mode 100644 index 00000000..4040bb36 --- /dev/null +++ b/docs/authoring/indexed-resources/runwhen-platform.md @@ -0,0 +1,51 @@ +# RunWhen platform indexed resources + +The **`runwhen`** platform exposes workspace-scoped resources for generation rules +that are not tied to a cloud object or Kubernetes CRD. + +## Resource types + +| Resource type | Scope | Description | +|---|---|---| +| `workspace` | One per workspace-builder run | Anchor for MCP tool-builder codebundles using `platform: runwhen` | + +## Template variables + +When a generation rule matches a `workspace` resource, templates receive: + +| Variable | Value | +|---|---| +| `match_resource.name` | Workspace name | +| `match_resource.qualified_name` | Workspace name | +| `match_resource.owner_email` | `workspaceOwnerEmail` from workspaceInfo | +| `workspace.name` | Workspace name (from base template variables) | +| `workspace.owner_email` | Owner email | +| `default_location` | Default runner location id | + +## Example generation rule + +```yaml +apiVersion: runwhen.com/v1 +kind: GenerationRules +spec: + platform: runwhen + generationRules: + - resourceTypes: + - workspace + matchRules: + - type: pattern + pattern: ".+" + properties: [name] + mode: substring + slxs: + - baseName: my-task + qualifiers: ["workspace"] + baseTemplateName: my-health-check + levelOfDetail: detailed + outputItems: + - type: slx + - type: runbook + templateName: my-health-check-taskset.yaml +``` + +See the RunWhen MCP `render_codecollection_skill` tool for generating this layout from a tested tool-builder script. diff --git a/generation-rules-guide.md b/generation-rules-guide.md index 7f876ead..13d6f884 100644 --- a/generation-rules-guide.md +++ b/generation-rules-guide.md @@ -6,7 +6,7 @@ apiVersion: runwhen.com/v1 kind: GenerationRules spec: - platform: # azure, kubernetes, gcp, aws + platform: # azure, kubernetes, gcp, aws, runwhen generationRules: - resourceTypes: [] matchRules: [] @@ -16,90 +16,101 @@ spec: ## Resource Type Specifications -### Basic Resource Types +Each rule block lists names under `resourceTypes`. The file-level **`spec.platform`** +(`azure`, `kubernetes`, `aws`, `gcp`, `runwhen`) is the default platform for +every entry in that file unless an entry overrides it. + +### Typical names (canonical + aliases) + +Indexers resolve names through per-platform registries. Use the **CloudQuery table +name** (canonical) or a documented **alias** — both work when `spec.platform` +is set: + ```yaml +# spec.platform: azure +resourceTypes: + - azure_compute_virtual_machines # canonical + - virtual_machine # alias → same type as above + - azure_appservice_web_apps + +# spec.platform: kubernetes +resourceTypes: + - deployment + - pod + - buckets.storage.gcp.upbound.io # CRD: plural.group[/version] + +# spec.platform: aws resourceTypes: - - resource_group # Simple name - - azure:resource_group # Platform-prefixed - - kubernetes:pod # Cross-platform + - aws_ec2_instances + - aws_s3_buckets + +# spec.platform: gcp +resourceTypes: + - gcp_compute_instances + - gcp_storage_buckets + +# spec.platform: runwhen (MCP tool-builder / workspace-scoped SLXs) +resourceTypes: + - workspace ``` -### Discovering Available Resource Types +Full lists: [`docs/authoring/indexed-resources/`](../docs/authoring/indexed-resources/) +and the generated `*-resource-catalog.md` files (regenerated from indexer code). + +### Optional platform override (`platform:type`) -To find the exact resource types available in your environment, examine your resource dump: +Only the **first** `:` splits platform from type. Use this when one entry must +target a different platform than `spec.platform` (uncommon): ```yaml -# Resource dump structure shows available resource types -platforms: - azure: !Platform - resourceTypes: - resource_group: !ResourceType # <- This is the resource type name to use - instances: {...} - virtual_machine: !ResourceType # <- Another available resource type - instances: {...} +spec: + platform: azure + generationRules: + - resourceTypes: + - resource_group # → azure (from spec.platform) + - kubernetes:deployment # → kubernetes (override for this entry) ``` -You can also use the CloudQuery documentation to find the full list of supported resource types for your specific provider: -- **Azure**: [CloudQuery Azure Plugin](https://www.cloudquery.io/docs/plugins/sources/azure/tables) - All Azure resource types -- **AWS**: [CloudQuery AWS Plugin](https://www.cloudquery.io/docs/plugins/sources/aws/tables) - All AWS resource types -- **GCP**: [CloudQuery GCP Plugin](https://www.cloudquery.io/docs/plugins/sources/gcp/tables) - All GCP resource types -- **Kubernetes**: [CloudQuery Kubernetes Plugin](https://www.cloudquery.io/docs/plugins/sources/k8s/tables) - All Kubernetes resource types - -> **Important**: The available resource types depend on which CloudQuery provider plugins you have configured and enabled in your environment. +This is **not** “cross-platform matching” in one rule — it selects which indexer +serves that entry. Prefer **separate generation-rule files** per `spec.platform`. -### Platform-Specific Resource Types +Kubernetes CRDs use `plural.group[/version]` (dots in the group; optional +`/version`). Do not confuse CRD syntax with `platform:type` overrides. -RunWhen Local supports **all CloudQuery resource types for the specific provider/platform** you're using. The resource type names correspond to the CloudQuery table schema names, but are typically simplified (e.g., `azure_resources_resource_groups` becomes `resource_group`). +### Dict form ```yaml -# Azure - Examples from CloudQuery Azure plugin -resourceTypes: - - resource_group # azure_resources_resource_groups - - virtual_machine # azure_compute_virtual_machines - - storage_account # azure_storage_accounts - - app_service # azure_web_apps - - sql_database # azure_sql_databases - - key_vault # azure_keyvault_vaults - - network_security_group # azure_network_security_groups - - virtual_network # azure_network_virtual_networks - - load_balancer # azure_network_load_balancers - -# Kubernetes - Examples from CloudQuery Kubernetes plugin -resourceTypes: - - pod # k8s_core_pods - - service # k8s_core_services - - deployment # k8s_apps_deployments - - namespace # k8s_core_namespaces - - configmap # k8s_core_config_maps - - secret # k8s_core_secrets - - ingress # k8s_networking_ingresses - - persistent_volume # k8s_core_persistent_volumes - - statefulset # k8s_apps_stateful_sets - -# AWS - Examples from CloudQuery AWS plugin -resourceTypes: - - ec2_instance # aws_ec2_instances - - s3_bucket # aws_s3_buckets - - rds_instance # aws_rds_instances - - lambda_function # aws_lambda_functions - - vpc # aws_ec2_vpcs - - security_group # aws_ec2_security_groups - - iam_role # aws_iam_roles - - cloudformation_stack # aws_cloudformation_stacks - - elb_load_balancer # aws_elbv2_load_balancers - -# GCP - Examples from CloudQuery GCP plugin resourceTypes: - - compute_instance # gcp_compute_instances - - storage_bucket # gcp_storage_buckets - - sql_instance # gcp_sql_instances - - kubernetes_cluster # gcp_container_clusters - - cloud_function # gcp_cloudfunctions_functions - - firewall_rule # gcp_compute_firewall_rules - - vpc_network # gcp_compute_networks + - resourceType: azure_keyvault_vaults + - platform: gcp + resourceType: gcp_compute_instances ``` -> **Note**: The exact resource type names may vary. Check your resource dump or CloudQuery documentation for the specific resource types available in your environment. +### Discovering available resource types + +**Review discovered resources in the Workspace Explorer UI** (workspace-builder on +port **8000**) — not by opening `resource-dump.yaml` on the filesystem: + +| Method | Use | +| --- | --- | +| [http://localhost:8000/explorer/](http://localhost:8000/explorer/) | Browse platforms, resource types, and instance payloads after discovery | +| `GET /explorer/api/summary` | JSON overview of indexed platforms and counts | +| `GET /explorer/api/resources?platform=…&resource_type=…` | Filter instances; inspect fields for `matchRules` paths | +| `docs/authoring/indexed-resources/*-resource-catalog.md` | Full type list from indexer registries (no cluster required) | + +See [Resource store query API](docs/architecture/resource-store-query-api.md) for +API details. Legacy YAML dumps under `shared/` are deprecated for authoring. + +CloudQuery plugin tables remain useful background for field shapes: + +- **Azure**: [CloudQuery Azure Plugin](https://www.cloudquery.io/docs/plugins/sources/azure/tables) +- **AWS**: [CloudQuery AWS Plugin](https://www.cloudquery.io/docs/plugins/sources/aws/tables) +- **GCP**: [CloudQuery GCP Plugin](https://www.cloudquery.io/docs/plugins/sources/gcp/tables) +- **Kubernetes**: [CloudQuery Kubernetes Plugin](https://www.cloudquery.io/docs/plugins/sources/k8s/tables) + +> **Important**: Which types appear in Explorer depends on your generation rules +> (selective discovery) and indexer backend — not every catalog row is indexed in +> every run. ## Match Rules @@ -424,8 +435,8 @@ All matched resources provide these template variables: ## Common Troubleshooting ### Issue: Generation Rule Not Matching -- Check that `resourceTypes` matches exactly what's in your resource dump -- Verify `properties` paths are correct (use `/` for nested paths) +- Confirm `resourceTypes` against Explorer (`http://localhost:8000/explorer/`) or the authoring catalogs — not legacy filesystem dumps +- Verify `properties` paths against a live resource payload in Explorer (use `/` for nested paths) - Test regex patterns with online regex testers - Enable debug logging to see match attempts diff --git a/scripts/aws/dump_aws_resource_catalog.py b/scripts/aws/dump_aws_resource_catalog.py new file mode 100644 index 00000000..a308bf12 --- /dev/null +++ b/scripts/aws/dump_aws_resource_catalog.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Regenerate ``docs/authoring/indexed-resources/aws-resource-catalog.md``. + +The catalog is the user-facing companion to ``aws.md``: a single sortable +table of every AWS resource type the ``awsapi`` indexer knows about, +grouped by service. It's derived directly from +``src/indexers/aws_resource_type_registry.yaml`` (which itself is generated +from the CloudQuery AWS plugin's table list and the manual overrides in +``scripts/aws/aws_resource_type_overrides.yaml``). + +Run this script after editing ``aws_resource_type_overrides.yaml`` and +re-running ``sync_aws_resource_type_registry.py``. Hand-edits to the +generated catalog get overwritten; touch the registry / overrides instead. + +Usage:: + + python scripts/aws/dump_aws_resource_catalog.py +""" + +from __future__ import annotations + +import datetime as _dt +import sys +from pathlib import Path + +_REPO_ROOT = Path(__file__).resolve().parents[2] +_SRC = _REPO_ROOT / "src" +sys.path.insert(0, str(_SRC)) + +from indexers.aws_resource_type_registry import load_registry # noqa: E402 + +_OUTPUT = ( + _REPO_ROOT + / "docs" + / "authoring" + / "indexed-resources" + / "aws-resource-catalog.md" +) + + +def main() -> None: + registry = load_registry() + rows = sorted( + registry, + key=lambda e: ((e.category or "~"), e.cloudquery_table_name), + ) + typed_count = sum(1 for e in rows if e.typed_collector) + generated_at = _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%d") + + lines: list[str] = [] + lines.append("# AWS resource catalog") + lines.append("") + lines.append( + "Every AWS resource type the native `awsapi` indexer can " + "discover. This page is the companion catalog for " + "[`aws.md`](./aws.md); see that page for how to enable the " + "indexer, what data each row carries, and the typed/generic " + "distinction." + ) + lines.append("") + lines.append( + f"_{len(rows)} resource types - {typed_count} typed (rich-payload), " + f"{len(rows) - typed_count} generic (Cloud Control envelope). " + f"Generated {generated_at} from " + "`src/indexers/aws_resource_type_registry.yaml`._" + ) + lines.append("") + lines.append( + "_Regenerate with `python scripts/aws/dump_aws_resource_catalog.py` " + "after touching the registry or overrides; do not hand-edit this file._" + ) + lines.append("") + lines.append( + "* `typed` - hand-written `boto3` collector returns a richer payload." + ) + lines.append( + "* `generic` - covered by the Cloud Control API catch-all when a " + "CloudFormation type exists; rows without a CFN type are registry-only " + "and skipped by generic discovery." + ) + lines.append("") + lines.append("| Service | CloudQuery table name | CFN type | Tier |") + lines.append("| --- | --- | --- | --- |") + for e in rows: + cat = e.category or "-" + cq = e.cloudquery_table_name + cfn = e.cfn_type or "-" + tier = "typed" if e.typed_collector else "generic" + lines.append(f"| {cat} | `{cq}` | `{cfn}` | {tier} |") + lines.append("") + + _OUTPUT.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"Wrote {_OUTPUT.relative_to(_REPO_ROOT)} ({len(rows)} entries)") + + +if __name__ == "__main__": + main() diff --git a/scripts/docs/sync_authoring_to_docs_site.py b/scripts/docs/sync_authoring_to_docs_site.py new file mode 100644 index 00000000..75d1691b --- /dev/null +++ b/scripts/docs/sync_authoring_to_docs_site.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python3 +"""Sync ``docs/authoring/`` markdown into the RunWhen docs site (Starlight). + +**Optional publish step only.** Canonical authoring reference stays in this repo +(``docs/authoring/`` + generated catalogs). MCP agents use the +``author-generation-rules`` skill, which points here — not docs.runwhen.com +legacy author pages. + +Use when the docs team is ready to replace stale ``/authors/generation-rules/`` +content on docs.runwhen.com with refreshed Starlight pages derived from this tree. + +Transforms for Starlight: + - Prepends YAML frontmatter (title, description, sidebar order/label) + - Strips the first ``# heading`` (title comes from frontmatter) + - Converts GitHub ``[!NOTE]`` alerts to Starlight ``:::note`` admonitions + - Rewrites internal links to docs-site paths + +Usage:: + + python scripts/docs/sync_authoring_to_docs_site.py + python scripts/docs/sync_authoring_to_docs_site.py --check # exit 1 if drift +""" + +from __future__ import annotations + +import argparse +import re +import sys +from dataclasses import dataclass +from pathlib import Path + +_REPO_ROOT = Path(__file__).resolve().parents[2] +_DEFAULT_DOCS_REPO = _REPO_ROOT.parent.parent / "runwhen" / "docs" +_AUTHORS_ROOT = "src/content/docs/authors" + + +@dataclass(frozen=True) +class PageSpec: + source: Path + dest_rel: str + title: str + description: str + sidebar_order: int + sidebar_label: str | None = None + sidebar_hidden: bool = False + + +_RWL_ARCH = "https://github.com/runwhen-contrib/runwhen-local/blob/main/docs/architecture" +_RWL_DOCS = "https://github.com/runwhen-contrib/runwhen-local/blob/main/docs" + + +def _pages() -> list[PageSpec]: + authoring = _REPO_ROOT / "docs" / "authoring" + return [ + PageSpec( + authoring / "concepts.md", + "concepts.md", + "Authoring Concepts", + "CodeBundle, Skill, SLX, and Runbook terminology for RunWhen Local authors.", + 1, + "Concepts", + ), + PageSpec( + authoring / "indexed-resources/README.md", + "indexed-resources/index.md", + "Indexed Resources", + "Overview of platform indexers and resource type catalogs for generation rules.", + 0, + "Overview", + ), + PageSpec( + authoring / "indexed-resources/azure.md", + "indexed-resources/azure.md", + "Azure Indexed Resources", + "What the azureapi indexer discovers and how generation rules match Azure resources.", + 1, + "Azure", + ), + PageSpec( + authoring / "indexed-resources/aws.md", + "indexed-resources/aws.md", + "AWS Indexed Resources", + "What the awsapi indexer discovers and how generation rules match AWS resources.", + 2, + "AWS", + ), + PageSpec( + authoring / "indexed-resources/gcp.md", + "indexed-resources/gcp.md", + "GCP Indexed Resources", + "What the gcpapi indexer discovers and how generation rules match GCP resources.", + 3, + "GCP", + ), + PageSpec( + authoring / "indexed-resources/kubernetes.md", + "indexed-resources/kubernetes.md", + "Kubernetes Indexed Resources", + "Built-in and CRD resource types the kubeapi indexer discovers.", + 4, + "Kubernetes", + ), + PageSpec( + authoring / "indexed-resources/runwhen-platform.md", + "indexed-resources/runwhen-platform.md", + "RunWhen Platform Resources", + "Workspace-scoped resources indexed under platform runwhen (MCP tool-builder output).", + 5, + "RunWhen Platform", + ), + PageSpec( + authoring / "indexed-resources/azure-resource-catalog.md", + "indexed-resources/azure-resource-catalog.md", + "Azure Resource Catalog", + "Sortable table of every Azure resource type the azureapi indexer knows.", + 10, + "Azure Catalog", + ), + PageSpec( + authoring / "indexed-resources/aws-resource-catalog.md", + "indexed-resources/aws-resource-catalog.md", + "AWS Resource Catalog", + "Sortable table of every AWS resource type the awsapi indexer knows.", + 11, + "AWS Catalog", + ), + PageSpec( + authoring / "indexed-resources/gcp-resource-catalog.md", + "indexed-resources/gcp-resource-catalog.md", + "GCP Resource Catalog", + "Sortable table of every GCP resource type the gcpapi indexer knows.", + 12, + "GCP Catalog", + ), + PageSpec( + authoring / "indexed-resources/kubernetes-resource-catalog.md", + "indexed-resources/kubernetes-resource-catalog.md", + "Kubernetes Resource Catalog", + "Built-in Kubernetes kinds available to generation rules.", + 13, + "Kubernetes Catalog", + ), + PageSpec( + authoring / "indexed-resources/runwhen-platform-resource-catalog.md", + "indexed-resources/runwhen-platform-resource-catalog.md", + "RunWhen Platform Resource Catalog", + "Resource types under platform runwhen.", + 14, + "RunWhen Catalog", + ), + PageSpec( + authoring / "generation-rules/README.md", + "generation-rules/schema.md", + "Generation Rules Schema", + "Schema reference for .runwhen/generation-rules YAML files.", + 1, + "Schema Reference", + ), + PageSpec( + _REPO_ROOT / "generation-rules-guide.md", + "generation-rules/syntax-reference.md", + "Generation Rules Syntax Reference", + "Complete matchRules, slxs, outputItems, and platform-specific syntax.", + 2, + "Syntax Reference", + ), + PageSpec( + authoring / "generation-rules/tag-hierarchy-contract.md", + "generation-rules/tag-hierarchy.md", + "Tag and Hierarchy Contract", + "Conventions for platform tag and hierarchy templates.", + 3, + "Tag Hierarchy", + ), + PageSpec( + authoring / "generation-rules/examples/README.md", + "generation-rules/examples/index.md", + "Generation Rule Examples", + "End-to-end generation rule examples by platform and pattern.", + 10, + "Examples Overview", + ), + PageSpec( + authoring / "generation-rules/examples/azure-keyvault-slx.md", + "generation-rules/examples/azure-keyvault-slx.md", + "Example: Azure Key Vault SLX", + "Generation rule for private Key Vaults with SKILL.md overlay.", + 11, + "Azure Key Vault", + ), + PageSpec( + authoring / "generation-rules/examples/azure-vm-disk-runbook.md", + "generation-rules/examples/azure-vm-disk-runbook.md", + "Example: Azure VM Disk Runbook", + "Match production VMs and expose OS disk IDs to templates.", + 12, + "Azure VM Disk", + ), + PageSpec( + authoring / "generation-rules/examples/kubernetes-deployment-slx.md", + "generation-rules/examples/kubernetes-deployment-slx.md", + "Example: Kubernetes Deployment SLX", + "Multi-replica Deployment rollout health check.", + 13, + "K8s Deployment", + ), + PageSpec( + authoring / "generation-rules/examples/multi-resource-runbook.md", + "generation-rules/examples/multi-resource-runbook.md", + "Example: Azure Web App Context", + "Pass related ARM IDs from a single matched resource into templates.", + 14, + "Web App Context", + ), + ] + + +_LINK_REWRITES: list[tuple[str, str]] = [ + (r"\(\./azure-resource-catalog\.md\)", "(/authors/indexed-resources/azure-resource-catalog/)"), + (r"\(\./aws-resource-catalog\.md\)", "(/authors/indexed-resources/aws-resource-catalog/)"), + (r"\(\./gcp-resource-catalog\.md\)", "(/authors/indexed-resources/gcp-resource-catalog/)"), + (r"\(\./kubernetes-resource-catalog\.md\)", "(/authors/indexed-resources/kubernetes-resource-catalog/)"), + (r"\(\./runwhen-platform-resource-catalog\.md\)", "(/authors/indexed-resources/runwhen-platform-resource-catalog/)"), + (r"\(\./README\.md\)", "(/authors/indexed-resources/)"), + (r"\(\./azure\.md\)", "(/authors/indexed-resources/azure/)"), + (r"\(\./aws\.md\)", "(/authors/indexed-resources/aws/)"), + (r"\(\./gcp\.md\)", "(/authors/indexed-resources/gcp/)"), + (r"\(\./kubernetes\.md\)", "(/authors/indexed-resources/kubernetes/)"), + (r"\(\./runwhen-platform\.md\)", "(/authors/indexed-resources/runwhen-platform/)"), + (r"\(\.\./indexed-resources/README\.md\)", "(/authors/indexed-resources/)"), + (r"\(\.\./indexed-resources/([^)]+)\)", r"(/authors/indexed-resources/\1/)"), + (r"\(\.\./README\.md\)", "(/authors/generation-rules/schema/)"), + (r"\(\.\./\.\./\.\./generation-rules-guide\.md\)", "(/authors/generation-rules/syntax-reference/)"), + (r"\(\./tag-hierarchy-contract\.md\)", "(/authors/generation-rules/tag-hierarchy/)"), + (r"\(\./examples/([^)]+)\)", r"(/authors/generation-rules/examples/\1/)"), + (r"\(\.\./examples/([^)]+)\)", r"(/authors/generation-rules/examples/\1/)"), + (r"\(\.\./\.\./architecture/([^)#]+)(#[^)]+)?\)", rf"({_RWL_ARCH}/\1\2)"), + (r"\(\.\./\.\./user-guide/([^)]+)\)", rf"({_RWL_DOCS}/user-guide/\1)"), + (r"\[([^\]]+)\]\(\.\./\.\./architecture/[^)]+\)", r"\1 (see runwhen-local architecture docs)"), + (r"\[([^\]]+)\]\(\.\./\.\./user-guide/[^)]+\)", r"\1"), +] + + +def _strip_leading_h1(text: str) -> str: + lines = text.splitlines() + if lines and lines[0].startswith("# "): + lines = lines[1:] + while lines and not lines[0].strip(): + lines = lines[1:] + return "\n".join(lines) + + +def _convert_admonitions(text: str) -> str: + out: list[str] = [] + i = 0 + lines = text.splitlines() + while i < len(lines): + line = lines[i] + note_match = re.match(r"^>\s*\[!NOTE\]\s*$", line) + if note_match: + out.append(":::note") + i += 1 + while i < len(lines) and lines[i].startswith(">"): + out.append(lines[i][1:].lstrip() if lines[i].startswith("> ") else lines[i][1:]) + i += 1 + out.append(":::") + continue + out.append(line) + i += 1 + return "\n".join(out) + + +def _rewrite_links(text: str) -> str: + for pattern, replacement in _LINK_REWRITES: + text = re.sub(pattern, replacement, text) + return text + + +def _render_page(spec: PageSpec) -> str: + raw = spec.source.read_text(encoding="utf-8") + body = _strip_leading_h1(raw) + body = _convert_admonitions(body) + body = _rewrite_links(body) + label_line = "" + if spec.sidebar_label: + label_line = f' label: "{spec.sidebar_label}"\n' + hidden_line = "" + if spec.sidebar_hidden: + hidden_line = " hidden: true\n" + frontmatter = ( + "---\n" + f'title: "{spec.title}"\n' + f'description: "{spec.description}"\n' + "sidebar:\n" + f" order: {spec.sidebar_order}\n" + f"{label_line}" + f"{hidden_line}" + "---\n\n" + ) + if body and not body.startswith("##"): + body = f"## Overview\n\n{body}" + return frontmatter + body + ("\n" if body else "") + + +def sync(docs_repo: Path, check: bool = False) -> int: + authors_dir = docs_repo / _AUTHORS_ROOT + if not authors_dir.is_dir(): + print(f"ERROR: docs authors dir not found: {authors_dir}", file=sys.stderr) + return 1 + + drift = 0 + for spec in _pages(): + if not spec.source.is_file(): + print(f"ERROR: missing source {spec.source}", file=sys.stderr) + return 1 + rendered = _render_page(spec) + dest = authors_dir / spec.dest_rel + dest.parent.mkdir(parents=True, exist_ok=True) + if dest.exists() and dest.read_text(encoding="utf-8") == rendered: + print(f"OK {spec.dest_rel}") + continue + if check: + print(f"DRIFT {spec.dest_rel}") + drift += 1 + continue + dest.write_text(rendered, encoding="utf-8") + print(f"WROTE {spec.dest_rel}") + + return 1 if drift else 0 + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--docs-repo", + type=Path, + default=_DEFAULT_DOCS_REPO, + help=f"Path to runwhen/docs checkout (default: {_DEFAULT_DOCS_REPO})", + ) + parser.add_argument( + "--check", + action="store_true", + help="Exit 1 if destination files differ from rendered output", + ) + args = parser.parse_args() + raise SystemExit(sync(args.docs_repo, check=args.check)) + + +if __name__ == "__main__": + main() diff --git a/scripts/gcp/dump_gcp_resource_catalog.py b/scripts/gcp/dump_gcp_resource_catalog.py new file mode 100644 index 00000000..8855d580 --- /dev/null +++ b/scripts/gcp/dump_gcp_resource_catalog.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Regenerate ``docs/authoring/indexed-resources/gcp-resource-catalog.md``. + +The catalog is the user-facing companion to ``gcp.md``: a single sortable +table of every GCP resource type the ``gcpapi`` indexer knows about, +grouped by service. It's derived directly from +``src/indexers/gcp_resource_type_registry.yaml`` (which itself is generated +from the CloudQuery GCP plugin's table list and the manual overrides in +``scripts/gcp/gcp_resource_type_overrides.yaml``). + +Run this script after editing ``gcp_resource_type_overrides.yaml`` and +re-running ``sync_gcp_resource_type_registry.py``. Hand-edits to the +generated catalog get overwritten; touch the registry / overrides instead. + +Usage:: + + python scripts/gcp/dump_gcp_resource_catalog.py +""" + +from __future__ import annotations + +import datetime as _dt +import sys +from pathlib import Path + +_REPO_ROOT = Path(__file__).resolve().parents[2] +_SRC = _REPO_ROOT / "src" +sys.path.insert(0, str(_SRC)) + +from indexers.gcp_resource_type_registry import load_registry # noqa: E402 + +_OUTPUT = ( + _REPO_ROOT + / "docs" + / "authoring" + / "indexed-resources" + / "gcp-resource-catalog.md" +) + + +def main() -> None: + registry = load_registry() + rows = sorted( + registry, + key=lambda e: ((e.category or "~"), e.cloudquery_table_name), + ) + typed_count = sum(1 for e in rows if e.typed_collector) + generated_at = _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%d") + + lines: list[str] = [] + lines.append("# GCP resource catalog") + lines.append("") + lines.append( + "Every GCP resource type the native `gcpapi` indexer can " + "discover. This page is the companion catalog for " + "[`gcp.md`](./gcp.md); see that page for how to enable the " + "indexer, what data each row carries, and the typed/generic " + "distinction." + ) + lines.append("") + lines.append( + f"_{len(rows)} resource types - {typed_count} typed (SDK collectors), " + f"{len(rows) - typed_count} generic (Cloud Asset Inventory pass). " + f"Generated {generated_at} from " + "`src/indexers/gcp_resource_type_registry.yaml`._" + ) + lines.append("") + lines.append( + "_Regenerate with `python scripts/gcp/dump_gcp_resource_catalog.py` " + "after touching the registry or overrides; do not hand-edit this file._" + ) + lines.append("") + lines.append( + "* `typed` - hand-written `google-cloud-*` collector; runs without CAI." + ) + lines.append( + "* `generic` - discoverable via the optional Cloud Asset Inventory " + "accelerator when a CAI asset type is mapped." + ) + lines.append("") + lines.append("| Service | CloudQuery table name | CAI asset type | Tier |") + lines.append("| --- | --- | --- | --- |") + for e in rows: + cat = e.category or "-" + cq = e.cloudquery_table_name + cai = e.cai_asset_type or "-" + tier = "typed" if e.typed_collector else "generic" + lines.append(f"| {cat} | `{cq}` | `{cai}` | {tier} |") + lines.append("") + + _OUTPUT.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"Wrote {_OUTPUT.relative_to(_REPO_ROOT)} ({len(rows)} entries)") + + +if __name__ == "__main__": + main() diff --git a/scripts/kubernetes/dump_kubernetes_resource_catalog.py b/scripts/kubernetes/dump_kubernetes_resource_catalog.py new file mode 100644 index 00000000..9752a046 --- /dev/null +++ b/scripts/kubernetes/dump_kubernetes_resource_catalog.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Regenerate ``docs/authoring/indexed-resources/kubernetes-resource-catalog.md``.""" + +from __future__ import annotations + +import datetime as _dt +import sys +from pathlib import Path + +_REPO_ROOT = Path(__file__).resolve().parents[2] +_SRC = _REPO_ROOT / "src" +sys.path.insert(0, str(_SRC)) + +from indexers.kubetypes import KubernetesResourceType # noqa: E402 + +_OUTPUT = ( + _REPO_ROOT + / "docs" + / "authoring" + / "indexed-resources" + / "kubernetes-resource-catalog.md" +) + + +def main() -> None: + generated_at = _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%d") + lines = [ + "# Kubernetes resource catalog", + "", + "Built-in Kubernetes resource types the kubeapi indexer discovers.", + "", + f"_Generated {generated_at} from `indexers/kubetypes.py`._", + "", + "Custom CRDs are declared in generation rules (`resourceTypes`) and indexed", + "selectively — they are not listed here.", + "", + "| Resource type | Notes |", + "|---|---|", + ] + for resource_type in KubernetesResourceType: + lines.append(f"| `{resource_type.value}` | Built-in Kubernetes kind |") + lines.extend(["",]) + _OUTPUT.parent.mkdir(parents=True, exist_ok=True) + _OUTPUT.write_text("\n".join(lines), encoding="utf-8") + print(f"Wrote {_OUTPUT}") + + +if __name__ == "__main__": + main() diff --git a/scripts/runwhen/dump_runwhen_resource_catalog.py b/scripts/runwhen/dump_runwhen_resource_catalog.py new file mode 100644 index 00000000..f6fe40f4 --- /dev/null +++ b/scripts/runwhen/dump_runwhen_resource_catalog.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +"""Regenerate ``docs/authoring/indexed-resources/runwhen-platform-resource-catalog.md``.""" + +from __future__ import annotations + +import datetime as _dt +from pathlib import Path + +_REPO_ROOT = Path(__file__).resolve().parents[2] +_OUTPUT = ( + _REPO_ROOT + / "docs" + / "authoring" + / "indexed-resources" + / "runwhen-platform-resource-catalog.md" +) + + +def main() -> None: + generated_at = _dt.datetime.now(_dt.timezone.utc).strftime("%Y-%m-%d") + lines = [ + "# RunWhen platform resource catalog", + "", + "Resource types indexed under `platform: runwhen`.", + "", + f"_Generated {generated_at} from `scripts/runwhen/dump_runwhen_resource_catalog.py`._", + "", + "| Resource type | Match names | Typed collector | Notes |", + "|---|---|---|---|", + "| `workspace` | `workspace` | yes | One instance per workspace-builder run |", + "", + "## match_resource properties (workspace)", + "", + "| Property | Description |", + "|---|---|", + "| `name` | Workspace name |", + "| `qualified_name` | Same as workspace name |", + "| `owner_email` | From workspaceInfo `workspaceOwnerEmail` |", + "| `location_id` | Default location id |", + "| `location_name` | Default location name |", + "", + ] + _OUTPUT.parent.mkdir(parents=True, exist_ok=True) + _OUTPUT.write_text("\n".join(lines), encoding="utf-8") + print(f"Wrote {_OUTPUT}") + + +if __name__ == "__main__": + main() diff --git a/src/component.py b/src/component.py index 83d24db9..92d07a6b 100644 --- a/src/component.py +++ b/src/component.py @@ -248,7 +248,7 @@ def init_components(): # be added here, which is less than ideal, although practically may not be # a huge deal. component_stages_init = ( - (Stage.INDEXER, ["load_resources", "kubeapi", "azureapi", "gcpapi", "awsapi", "cloudquery", "azure_devops"]), + (Stage.INDEXER, ["load_resources", "kubeapi", "azureapi", "gcpapi", "awsapi", "cloudquery", "azure_devops", "runwhen_platform"]), (Stage.ENRICHER, ["generation_rules"]), (Stage.RENDERER, ["render_output_items", "dump_resources"]) ) diff --git a/src/enrichers/generation_rules.py b/src/enrichers/generation_rules.py index ac05d508..e45062e8 100644 --- a/src/enrichers/generation_rules.py +++ b/src/enrichers/generation_rules.py @@ -35,6 +35,7 @@ from .gcp import GCPPlatformHandler, GCP_PLATFORM from .aws import AWSPlatformHandler, AWS_PLATFORM from .azure_devops import AzureDevOpsPlatformHandler +from .runwhen_platform import RunWhenPlatformHandler, RUNWHEN_PLATFORM from renderers.render_output_items import OUTPUT_ITEMS_PROPERTY from renderers.render_output_items import OutputItem as RendererOutputItem @@ -1183,6 +1184,7 @@ def load(context: Context) -> None: GCP_PLATFORM: GCPPlatformHandler(), AWS_PLATFORM: AWSPlatformHandler(), "azure_devops": AzureDevOpsPlatformHandler(), + RUNWHEN_PLATFORM: RunWhenPlatformHandler(), } context.set_property(PLATFORM_HANDLERS_PROPERTY_NAME, platform_handlers) request_code_collections = context.get_setting("CODE_COLLECTIONS") diff --git a/src/enrichers/runwhen_platform.py b/src/enrichers/runwhen_platform.py new file mode 100644 index 00000000..587a7554 --- /dev/null +++ b/src/enrichers/runwhen_platform.py @@ -0,0 +1,54 @@ +"""Platform handler for RunWhen platform generation rules.""" + +from __future__ import annotations + +from typing import Any, Optional + +from resources import Resource + +from .generation_rule_types import LevelOfDetail, PlatformHandler + +RUNWHEN_PLATFORM = "runwhen" +WORKSPACE_RESOURCE_TYPE = "workspace" + + +class RunWhenPlatformHandler(PlatformHandler): + def __init__(self) -> None: + super().__init__(RUNWHEN_PLATFORM) + + def get_level_of_detail(self, resource: Resource) -> LevelOfDetail: + return LevelOfDetail.DETAILED + + def get_resource_qualifier_value(self, resource: Resource, qualifier_name: str) -> Optional[str]: + if qualifier_name in ("workspace", "resource", "name"): + return resource.name + if qualifier_name == "owner_email": + return getattr(resource, "owner_email", None) + if qualifier_name == "location_id": + return getattr(resource, "location_id", None) + if qualifier_name == "location_name": + return getattr(resource, "location_name", None) + return None + + def get_resource_property_values(self, resource: Resource, property_name: str) -> Optional[list[Any]]: + property_name = property_name.lower() + if property_name == "name": + return [resource.name] + value = getattr(resource, property_name, None) + if value is not None: + return [value] + return None + + def get_standard_template_variables(self, resource: Resource) -> dict[str, Any]: + return { + "workspace_resource": { + "name": resource.name, + "qualified_name": resource.qualified_name, + "owner_email": getattr(resource, "owner_email", None), + "location_id": getattr(resource, "location_id", None), + "location_name": getattr(resource, "location_name", None), + } + } + + def resolve_template_variable_value(self, resource: Resource, variable_name: str) -> Optional[Any]: + return self.get_resource_qualifier_value(resource, variable_name) diff --git a/src/indexers/runwhen_platform.py b/src/indexers/runwhen_platform.py new file mode 100644 index 00000000..fde55af6 --- /dev/null +++ b/src/indexers/runwhen_platform.py @@ -0,0 +1,54 @@ +"""Index RunWhen platform resources for generation-rule matching. + +The ``runwhen`` platform exposes workspace-scoped resources so generation rules +can render SLXs without tying discovery to a cloud or Kubernetes object. +""" + +from __future__ import annotations + +import logging + +from component import Context +from resources import REGISTRY_PROPERTY_NAME, Registry + +logger = logging.getLogger(__name__) + +DOCUMENTATION = ( + "Index RunWhen platform resources (workspace anchor) for generation rules " + "with platform: runwhen" +) + +RUNWHEN_PLATFORM = "runwhen" +WORKSPACE_RESOURCE_TYPE = "workspace" + + +def index(context: Context) -> None: + workspace_name = context.get_setting("WORKSPACE_NAME") + workspace_owner_email = context.get_setting("WORKSPACE_OWNER_EMAIL") + location_id = context.get_setting("LOCATION_ID") + location_name = context.get_setting("LOCATION_NAME") + + if not workspace_name: + logger.warning("WORKSPACE_NAME not set; skipping runwhen platform indexing") + return + + registry: Registry = context.get_property(REGISTRY_PROPERTY_NAME) + attributes = { + "owner_email": workspace_owner_email, + "short_name": workspace_name, + "location_id": location_id, + "location_name": location_name, + "platform": RUNWHEN_PLATFORM, + "resource_type": WORKSPACE_RESOURCE_TYPE, + } + registry.add_resource( + RUNWHEN_PLATFORM, + WORKSPACE_RESOURCE_TYPE, + workspace_name, + workspace_name, + attributes, + ) + logger.info( + "Indexed runwhen platform workspace resource: name=%s", + workspace_name, + ) diff --git a/src/indexers/test_runwhen_platform.py b/src/indexers/test_runwhen_platform.py new file mode 100644 index 00000000..eb691de7 --- /dev/null +++ b/src/indexers/test_runwhen_platform.py @@ -0,0 +1,76 @@ +"""Tests for the runwhen platform indexer and platform handler.""" + +from __future__ import annotations + +import os +import sys +from unittest import TestCase +from unittest.mock import MagicMock + +_THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +_SRC_DIR = os.path.dirname(_THIS_DIR) +if _SRC_DIR not in sys.path: + sys.path.insert(0, _SRC_DIR) + +from enrichers.runwhen_platform import ( # noqa: E402 + RUNWHEN_PLATFORM, + WORKSPACE_RESOURCE_TYPE, + RunWhenPlatformHandler, +) +from indexers import runwhen_platform # noqa: E402 +from resources import REGISTRY_PROPERTY_NAME, Registry, ResourceTypeSpec # noqa: E402 + + +class RunWhenPlatformIndexerTests(TestCase): + def test_index_adds_workspace_resource(self) -> None: + registry = Registry() + context = MagicMock() + context.get_property.return_value = registry + + def _setting(name: str): + values = { + "WORKSPACE_NAME": "demo-workspace", + "WORKSPACE_OWNER_EMAIL": "ops@example.com", + "LOCATION_ID": "loc-123", + "LOCATION_NAME": "shared-cluster", + } + return values.get(name) + + context.get_setting.side_effect = _setting + + runwhen_platform.index(context) + + resource_type = registry.lookup_resource_type(RUNWHEN_PLATFORM, WORKSPACE_RESOURCE_TYPE) + assert resource_type is not None + assert len(resource_type.instances) == 1 + resource = next(iter(resource_type.instances.values())) + assert resource.name == "demo-workspace" + assert resource.qualified_name == "demo-workspace" + assert resource.owner_email == "ops@example.com" + + +class RunWhenPlatformHandlerTests(TestCase): + def setUp(self) -> None: + self.handler = RunWhenPlatformHandler() + self.registry = Registry() + self.registry.add_resource( + RUNWHEN_PLATFORM, + WORKSPACE_RESOURCE_TYPE, + "demo-workspace", + "demo-workspace", + {"owner_email": "ops@example.com"}, + ) + self.context = MagicMock() + self.context.get_property.return_value = self.registry + + def test_get_resources_returns_workspace_instance(self) -> None: + spec = ResourceTypeSpec.construct_from_config("workspace", RUNWHEN_PLATFORM) + resources = list(self.handler.get_resources(spec, self.context)) + assert len(resources) == 1 + assert resources[0].name == "demo-workspace" + + def test_get_resource_qualifier_value_workspace(self) -> None: + spec = ResourceTypeSpec.construct_from_config("workspace", RUNWHEN_PLATFORM) + resource = list(self.handler.get_resources(spec, self.context))[0] + assert self.handler.get_resource_qualifier_value(resource, "workspace") == "demo-workspace" + assert self.handler.get_resource_property_values(resource, "name") == ["demo-workspace"] diff --git a/src/run.sh b/src/run.sh index 116199ad..3374744b 100755 --- a/src/run.sh +++ b/src/run.sh @@ -60,9 +60,9 @@ touch "$LOCK_FILE" # workspaceInfo.yaml (azureIndexerBackend=cloudquery, gcpIndexerBackend=cloudquery, # awsIndexerBackend=cloudquery). CloudQuery is still included below so the # override keeps working; by default it is a no-op for all three clouds. -COMPONENTS="load_resources,kubeapi,azureapi,gcpapi,awsapi,azure_devops,generation_rules,render_output_items,dump_resources" +COMPONENTS="load_resources,kubeapi,azureapi,gcpapi,awsapi,azure_devops,runwhen_platform,generation_rules,render_output_items,dump_resources" if [ $DISABLE_CLOUDQUERY -eq 0 ]; then - COMPONENTS="load_resources,kubeapi,azureapi,gcpapi,awsapi,cloudquery,azure_devops,generation_rules,render_output_items,dump_resources" + COMPONENTS="load_resources,kubeapi,azureapi,gcpapi,awsapi,cloudquery,azure_devops,runwhen_platform,generation_rules,render_output_items,dump_resources" fi # Run the Python script with your specified arguments