PSCumulus Strategy¶
Synopsis¶
PSCumulus is a thin cross-cloud PowerShell abstraction built for the PowerShell + DevOps Global Summit 2026 session:
"Cross-Cloud without Crossed Fingers: Surviving Azure, AWS, and GCP with PowerShell"
This document collects the project rationale, module boundaries, normalization rules, and roadmap in one place.
Description¶
The project is built around a simple idea:
Build on what does not move.
The clouds differ wildly. PowerShell does not. PSCumulus uses the PowerShell verb-noun model as a stable lens for querying and operating across Azure, AWS, and GCP without pretending the providers are identical.
This is intentionally not a full cloud framework. It is a narrow, honest abstraction aimed at interactive operations, demos, and cross-cloud discovery.
Module Scope¶
The public surface focuses on a small set of cross-cloud tasks where the user intent is stable:
| Command | Intent |
|---|---|
Connect-Cloud |
Prepare a ready-to-use cloud session, including auth if needed |
Disconnect-Cloud |
Clear stored session context for a selected provider |
Export-CloudInventory |
Export connected inventory to JSON or CSV |
Find-CloudResource |
Search by name across connected providers and resource kinds |
Get-CloudContext |
Inspect established provider sessions for the current shell |
Get-CloudInstance |
Enumerate compute instances, optionally filtered by exact name within scope |
Get-CloudStorage |
Enumerate storage resources |
Get-CloudTag |
Enumerate tags or labels |
Get-CloudNetwork |
Enumerate virtual networks |
Get-CloudDisk |
Enumerate disks or volumes |
Get-CloudFunction |
Enumerate serverless functions |
Get-CloudRegion |
List supported Azure, AWS, and GCP regions |
Resolve-CloudPath |
Parse a cloud path string into a structured CloudPath object |
Restart-CloudInstance |
Restart a compute instance |
Set-CloudTag |
Set tags or labels on a resource |
Start-CloudInstance |
Start a compute instance |
Stop-CloudInstance |
Stop a compute instance |
Test-CloudConnection |
Test connectivity to one or all providers |
What This Repo Already Was¶
Before the recent typed-contract and record-model work, PSCumulus was already a working cmdlet-first module with a clear shape:
- a small public surface built around verb-noun commands like
Get-CloudInstance - provider-specific backend functions under
Private/ - normalized
PSCumulus.CloudRecordoutput built from a stable shared property contract - a stored multi-provider session context for interactive use
- honest provider-native detail preserved in
Metadata
That matters because the current evolution work is not trying to replace the original idea. It is trying to strengthen it.
The repo was already proving a useful thesis:
- PowerShell's cmdlet model is a stable cross-cloud interface
- normalized records make pipelines practical across Azure, AWS, and GCP
- a narrow abstraction is more honest than pretending every cloud concept is universal
The recent changes do not invalidate that model. They tighten its internal correctness and prepare it for future additive capabilities.
Provider Strategy¶
- Azure: wrap
Az.*modules - AWS: wrap
AWS.Tools.*modules - GCP: wrap
gcloud ... --format=json
GCP uses the CLI adapter path deliberately. It provides stable JSON output and aligns with the most common authentication flow users already have on hand.
Normalization Strategy¶
The test behind every unified command: do the underlying CSP philosophies behind this concept overlap enough that a normalized answer is still honest?
- For compute, storage, disk, network, functions, and tags, yes. The question translates. The answer can be normalized.
- For IAM, the question is the same. The answer cannot be. AWS thinks in policy documents. Azure thinks in role assignments scoped to a resource hierarchy. GCP thinks in bindings. Forcing a single surface over those would erase distinctions that matter in practice.
Provider-native differences that survive normalization do not belong in the public command noun.
Stage 2 sharpened that principle: genuinely opaque detail stays in Metadata, but commonly-needed vendor identity fields graduate into first-class properties on vendor subclasses.
Shared Output Contract¶
Inventory commands return PSCumulus.CloudRecord-compatible records with a stable cross-cloud shape:
| Field | Description |
|---|---|
Name |
Resource name |
Provider |
Azure, AWS, or GCP |
Region |
Region or zone |
Status |
Normalized semantic state |
Size |
SKU, instance type, or storage class |
CreatedAt |
Creation time when available |
PrivateIpAddress |
Private IP address when available |
PublicIpAddress |
Public IP address when available |
Tags |
Normalized hashtable. AWS tags, Azure tags, and GCP labels all map here |
Metadata |
Provider-native details that do not normalize cleanly |
Status is semantic, not just title-cased provider output. Examples:
- AWS
shutting-downbecomesTerminating - Azure
VM deallocatedbecomesStopped - GCP
TERMINATEDbecomesStopped
That GCP mapping is deliberate: native GCP TERMINATED means stopped-but-restartable, while normalized Terminated is reserved for permanently gone resources. The original provider value remains available in Metadata.NativeStatus.
Suspending and Suspended are valid normalized states today, but they currently come from GCP only.
For Azure instances, when no power state can be read, PSCumulus now emits Unknown rather than the older Ready fallback so the public status contract stays aligned with the semantic enum vocabulary.
What Belongs On Vendor Subclasses¶
- Azure instance records:
ResourceGroup,VmId,OsType - AWS instance records:
InstanceId,VpcId,SubnetId - GCP instance records:
Project,Zone,Id
What Belongs In Metadata¶
- native status strings
- provider-native long-tail details that do not deserve a stable first-class property
- native identifiers and shape details that are useful but not common enough to deserve a stable property
What Not To Normalize¶
Do not force a shared command when the providers express materially different models. IAM is the clearest example. The human question is the same ("who can do what?"), but the CSP answers are structured so differently that a normalized surface would be dishonest:
Get-AzRoleAssignment -Scope "/subscriptions/..."
Get-IAMPolicy -UserName "adil"
gcloud projects get-iam-policy my-project
Explicit non-goals:
- IAM, RBAC, policies, and bindings
- Advanced networking concepts with incompatible semantics
- Billing and cost models
- Full provisioning coverage
- Perfect feature parity across all providers
Rule of thumb: if the normalized object would be mostly Metadata, the abstraction is too weak to deserve a first-class public command.
Connection Lifecycle¶
Connect-Cloud is not just a dispatcher. It owns the full session readiness workflow:
- Verify the required provider tools are installed
- Detect whether an active authentication session exists
- If not authenticated, trigger the provider-native login flow automatically
- Store a normalized per-provider context (account identity, scope, region)
- Set the active provider for the current session
The per-provider detection works differently for each provider because the providers are genuinely different:
- Azure: checks
Get-AzContext; callsConnect-AzAccountif no session exists, and can pass tenant and subscription selectors when provided - AWS: checks environment variables and
~/.awscredential files; proceeds throughInitialize-AWSDefaultConfiguration - GCP: checks
gcloud auth listfor an active account; callsgcloud auth application-default loginif none is found
Session context is stored per provider, not as a single active slot. This means connecting to Azure, then AWS, then GCP leaves all three contexts available. Get-CloudContext surfaces all of them, and Disconnect-Cloud can clear one provider without disturbing the others.
Ergonomics¶
After Connect-Cloud, the module remembers the active provider for the current session. Disconnect-Cloud recalculates the active provider when the active one is removed. Public commands can often omit -Provider when:
- the parameter set already implies the provider
- the active session provider makes the intent unambiguous
This keeps interactive usage fast without making scripts depend on hidden state.
Implementation Stages¶
PSCumulus is being built in additive stages so each one is shippable on its own. The cmdlets remain the primary interface throughout. Any future Provider is layered on top of the same backend engine, not treated as a replacement. The core module stays PowerShell 5.1-compatible, while later navigation work is expected to target PowerShell 7+ where provider classes are more reliable.
This direction became much clearer after the Summit talk on Monday, April 13, 2026, when Jeffrey Snover offered the insight that unlocked the roadmap: use a base class for shared properties, subclass per vendor, and let the subclass own parsing. The future Provider remains in the roadmap, but it now follows the corrected record model instead of leading it. The full rationale and stage-by-stage narrative live in Evolution.
Current status: v0.6.1 is the documentation narrative refresh for the completed Stages 1, 2, 3, and v0.6.0 hardening pass.
Broad outline:
- Stage 0: Cmdlet Contract: prove that a narrow verb-noun surface can make common cross-cloud operations feel coherent.
- Stage 1: Internal Typed Contract: strengthen status and tag correctness without changing the public cmdlet surface.
- Stage 2: Vendor Subclass Records: introduce a real base record class, vendor subclasses, subclass-owned normalization factories, and
Kind. - Stage 3: Cloud Path Model: define a structured path/resolver layer independent of any Provider mechanics.
- Stage 3.5: v0.6.0 Hardening And Cross-Cloud Helpers: add search, inventory export, region data, lifecycle consistency, completer/doc cleanup, and deliberate scope cuts before attempting navigation.
- Stage 4: The Provider (Read-Only): add additive navigation over the same backend engine.
- Stage 5: Write Operations Through the Provider: let lifecycle actions flow through path context once navigation is stable.
- Stage 6: Cross-Cloud Navigation And Aggregation: expose multi-provider views through navigation as well as cmdlets.
The v0.6.0 hardening pass is important enough to name because it explains why the module got smaller in some places and more complete in others. Find-CloudResource, Export-CloudInventory, and Get-CloudRegion landed because they reinforce the existing cmdlet story. Get-CloudSnapshot, Get-CloudImage, Remove-CloudTag, and Set-CloudTag -Path were held back because they were not ready to carry the same promise.
For the full stage-by-stage plan, rationale, origin story, and decision details, see Evolution.
Work intentionally left out:
- A fake universal IAM surface
- Terraform-style provisioning
- Abstractions that erase important provider semantics