| name | add-azure-resource-support |
| description | Add support for a new Azure resource in cloud-terrastodon. Use when creating Azure resource newtypes, typed resource/id/name models, naming-rule validation, Resource Graph list requests, Azure CLI list/show commands, scope plumbing, outage/discovery matching, and repo-standard validation with .\\check-all.ps1. |
| argument-hint | Resource kind to add, for example: network interface, load balancer, subnet child resource |
| user-invocable | true |
Add Azure Resource Support
Use this skill when extending cloud-terrastodon to support a new Azure resource end to end.
This workflow captures the repo pattern used for resources like:
- Azure Public IP
- Azure Application Gateway
- Azure Application Gateway backend health
- Azure Network Interface
What This Produces
- Strong
azure_types models for the resource
- Typed
Id and Name newtypes with Azure naming validation
Scope integration when the resource has a first-class ARM resource id
- A typed request in
crates/azure for listing or fetching the resource
ct az ... list and ct az ... show CLI plumbing in crates/entrypoint
- Matching logic for resource id, name, and useful secondary identifiers like IP or FQDN
- Tests and repo validation via
./check-all.ps1
When To Use
- Add a new top-level Azure ARM resource to the repo
- Replace
String ids or names with strong newtypes
- Add Azure discovery support for outage investigation or correlation workflows
- Mirror an existing Azure resource pattern for consistency
Procedure
1. Pick the Closest Existing Pattern
Start by finding the nearest comparable resource already in the repo.
Good reference points:
crates/azure_types/src/azure_public_ip_resource.rs
crates/azure_types/src/azure_public_ip_resource_id.rs
crates/azure_types/src/azure_public_ip_resource_name.rs
crates/azure/src/public_ip_list_request.rs
crates/entrypoint/src/cli/command/azure/public_ip/
crates/azure_types/src/azure_application_gateway_resource.rs
crates/azure/src/application_gateway_backend_health_request.rs
Choose the reference based on shape:
- Top-level ARM resource with list/show only: mirror Public IP
- Top-level ARM resource with additional action endpoint: mirror Application Gateway plus backend health
- Correlation/discovery data source: mirror how
outage_investigate.rs enriches and matches data
2. Define The Resource Surface
Decide what support is needed right now.
Questions to answer:
- Do we need a top-level resource type?
- Do we need
Id and Name newtypes?
- Is there a separate action response type, like backend health?
- Do users need
list, show, or both?
- Should
show match only id/name, or also IP/FQDN/child-resource references?
- Does this resource belong in
ScopeImplKind and ScopeImpl?
Default rule:
- If the resource has a stable ARM id and will be passed around in code, add strong
Id and Name types.
3. Add Name Newtype In azure_types
Create crates/azure_types/src/<resource>_name.rs.
Follow the repo slug pattern:
- Implement
Slug
- Validate using Azure naming rules from Microsoft docs
- Add
FromStr, Display, serde, Deref, TryFrom<CompactString>
- Add property tests or fuzz tests when the adjacent resources do so
Naming rules checklist:
- Confirm min/max length
- Confirm allowed characters
- Confirm start/end constraints
- Add the Microsoft naming-rules documentation URL in wrapped errors
4. Add Id Newtype In azure_types
Create crates/azure_types/src/<resource>_id.rs.
Use the repo resource-id pattern:
- Add the ARM suffix constant like
.../providers/Microsoft.Network/...
- Store
resource_group_id plus typed name for resource-group-scoped resources
- Implement
HasSlug, AsRef, NameValidatable, HasPrefix, TryFromResourceGroupScoped
- Implement
Scope
- Implement serde via expanded ARM id strings
- Add round-trip and constructor tests
If the resource is a first-class scope:
- Add it to
ScopeImplKind
- Add it to
ScopeImpl
- Update scope parsing, display,
expanded_form, short_form, and kind mapping in crates/azure_types/src/scopes.rs
5. Add The Typed Resource In azure_types
Create crates/azure_types/src/<resource>.rs.
Model the JSON returned by Resource Graph or the ARM API.
Rules:
- Use typed
id and name fields when available
- Preserve
tenant_id when the repo pattern expects it
- Use
deserialize_default_if_null for arrays and maps that Azure may return as null
- Prefer typed nested structs for fields the repo will actually use
- Leave unknown or low-value areas as
serde_json::Value until there is a clear need
When choosing how much to model:
- Strong-type fields needed for CLI filtering, correlation, or follow-up requests
- Keep the first pass focused on fields the workflow needs now
- Expand later when real payloads justify more structure
6. Export The New Types
Update crates/azure_types/src/lib.rs.
Required changes:
- Add
mod ...;
- Add
pub use crate::...::*;
7. Add The Azure Request In crates/azure
For list support, add a file like crates/azure/src/<resource>_list_request.rs.
Use the ResourceGraphHelper pattern:
- Define a request struct holding
tenant_id
- Expose
fetch_all_<resources>(tenant_id)
- Implement
CacheableCommand
- Build a Resource Graph query selecting the typed fields you modeled
- Return
Vec<YourResourceType>
- Add an integration-style test if similar nearby files do
For action endpoints or non-graph fetches:
- Follow the backend-health pattern
- Use typed REST responses instead of raw
Value when possible
- Reuse shared REST plumbing from
cloud_terrastodon_credentials
8. Export The Request From crates/azure
Update crates/azure/src/lib.rs.
Required changes:
- Add the new
mod
- Re-export it with
pub use
9. Add CLI Module Plumbing In entrypoint
Create a folder under crates/entrypoint/src/cli/command/azure/ when the resource deserves its own command group.
Typical files:
mod.rs
azure_<resource>.rs
azure_<resource>_list.rs
azure_<resource>_show.rs
Then wire them into:
crates/entrypoint/src/cli/command/azure/mod.rs
crates/entrypoint/src/cli/command/azure/azure_command.rs
Alias guidance:
- Add a short alias only when it is obvious and consistent, like
agw or nic
10. Implement list
Mirror the public-IP list command.
Expected behavior:
- Resolve
tenant
- Call the typed request
- Log count with
tracing::info!
- Print pretty JSON to stdout
11. Implement show
Mirror the public-IP or application-gateway show command.
Base matching always includes:
- Full resource id
- Resource name, case-insensitive
Optional matching may include:
- IP addresses
- FQDNs
- Attached public IP resource ids
- Child resource ids
Behavior rules:
0 matches: clear not-found error
1 match: print JSON
>1 matches: sort by expanded id and ask for full resource id
12. Add Correlation Hooks If The Resource Supports Discovery Workflows
If the new resource helps outage investigation or automated digging, update the relevant workflow.
Examples:
- Public IP -> Application Gateway frontend
- Backend server IP -> Network Interface
- Network Interface -> VM
When enriching a report:
- Deduplicate expensive follow-up lookups
- Preserve partial success by attaching an
error field instead of failing the whole report when helpful
- Use typed response models in the report output
13. Use Strong Types Instead Of String Where Worthwhile
Prefer strong types for:
- ARM resource ids
- Resource names
- Enumerated health states or status values
Use tolerant enums when Azure responses are inconsistent across docs and real payloads.
Example strategy:
- Add known enum variants
- Add
Other(String) to preserve forward compatibility
14. Test At The Right Level
Add or update tests in the new files.
Common test categories:
- Resource name validation
- Resource id round-trip parsing/serialization
- Typed resource deserialization from a realistic payload sample
- CLI matching behavior helpers
- Case-insensitive header or id lookups if relevant
Use real payload fragments whenever possible.
15. Validate With Repo Standard Checks
Always run the repo’s preferred validation flow:
.\check-all.ps1
When tests use CommandKind::CloudTerrastodon, remember the repo note:
- build the binary first if CLI command shapes changed
Decision Points
Whether To Add Id And Name Types
Add them when:
- The resource is a first-class Azure resource
- The resource appears in CLI input or output
- The resource will be referenced by other typed structs
Skip or defer only when:
- The data is purely nested and not reused
- The workflow is exploratory and not stable yet
Whether To Add Scope Support
Add scope support when:
- The resource has a standard ARM id
- The repo benefits from parsing or displaying it generically
- Other commands may accept it as a scope-like input later
Whether To Match On Secondary Identifiers In show
Add secondary matching when users naturally search that way.
Examples:
- Public IP
show by IP or FQDN
- NIC
show by private IP or attached public IP resource id
Do not add overly broad fuzzy matching that can create noisy ambiguity.
Whether To Strong-Type A Field Now
Strong-type it now when:
- Code branches on it
- It appears in multiple workflows
- It drives filtering or diagnostics
Leave it as String or Value for now when:
- Only displayed raw
- Documentation and observed payloads diverge too much and no behavior depends on it yet
- Real-world examples are still sparse
Completion Criteria
A resource addition is complete when all of the following are true:
- Typed resource compiles and deserializes from realistic payloads
Id and Name newtypes exist and validate correctly
azure_types and azure exports are wired
- CLI command is reachable from
ct az ...
list and/or show behave like sibling commands in the repo
- Scope plumbing is updated if needed
- New matching logic is covered by tests where practical
./check-all.ps1 passes
Examples
- Add support for Azure load balancers using the Public IP and Network Interface patterns
- Add
ct az nic show <private-ip> and thread NIC matches into outage investigation
- Add a typed action response like Application Gateway backend health instead of returning raw
Value
Notes For This Repo
- Prefer
apply_patch for focused file edits
- Prefer ASCII in new files
- Reuse existing repo patterns rather than inventing a new abstraction layer
- Do not duplicate REST response types already provided by
cloud_terrastodon_credentials
- Use
deserialize_default_if_null aggressively for Azure arrays and maps that may come back as null
- Validate end-to-end with
./check-all.ps1