Public/storage-pool.ps1

# Primary storage pools: create/list/update/delete, maintenance mode, scope
# changes, migration lookups, and the metrics/providers/objects list commands.

function Get-CSStoragePool {
    <#
    .SYNOPSIS
        Lists primary storage pools.

    .DESCRIPTION
        Wraps listStoragePools. Filter by zone, pod, cluster, or by the pool's own
        id/name. Zone and cluster objects can be piped in to scope the list.

    .PARAMETER Id
        Filter by storage pool ID

    .PARAMETER Name
        Filter by storage pool name

    .PARAMETER ZoneId
        Filter by zone ID

    .PARAMETER PodId
        Filter by pod ID

    .PARAMETER ClusterId
        Filter by cluster ID

    .PARAMETER IpAddress
        Filter by the pool's IP address

    .PARAMETER Path
        Filter by the pool's path

    .PARAMETER Scope
        Filter by scope: Zone, Cluster, or Host

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSStoragePool -ZoneId $zoneId
        Lists every primary storage pool in a zone.

    .EXAMPLE
        Get-CSCluster -Name 'kvm-01' | Get-CSStoragePool
        Lists the pools on a cluster piped in by object.
    #>

    [CmdletBinding()]
    param(
        [string]$Id,

        [string]$Name,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ZoneId,

        [string]$PodId,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ClusterId,

        [string]$IpAddress,

        [string]$Path,

        [ValidateSet('Zone', 'Cluster', 'Host')]
        [string]$Scope,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{}
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Id = 'id'; Name = 'name'; ZoneId = 'zoneid'; PodId = 'podid'; ClusterId = 'clusterid'; IpAddress = 'ipaddress'
            Path = 'path'; Scope = 'scope'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listStoragePools' -Parameters $apiParams) -Command 'listStoragePools'
    }
}

function Get-CSStoragePoolMetric {
    <#
    .SYNOPSIS
        Lists primary storage pools with capacity and IOPS metrics.

    .DESCRIPTION
        Wraps listStoragePoolsMetrics: the same rows as Get-CSStoragePool plus
        allocated/used capacity and IOPS figures.

    .PARAMETER Id
        Filter by storage pool ID

    .PARAMETER Name
        Filter by storage pool name

    .PARAMETER ZoneId
        Filter by zone ID

    .PARAMETER PodId
        Filter by pod ID

    .PARAMETER ClusterId
        Filter by cluster ID

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSStoragePoolMetric -ZoneId $zoneId | Sort-Object disksizeusedpercentage -Descending
        Finds the fullest storage pools in a zone.
    #>

    [CmdletBinding()]
    param(
        [string]$Id,

        [string]$Name,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$ZoneId,

        [string]$PodId,

        [string]$ClusterId,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{}
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Id = 'id'; Name = 'name'; ZoneId = 'zoneid'; PodId = 'podid'; ClusterId = 'clusterid'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listStoragePoolsMetrics' -Parameters $apiParams) -Command 'listStoragePoolsMetrics'
    }
}

function Get-CSStorageTag {
    <#
    .SYNOPSIS
        Lists the storage tags in use.

    .DESCRIPTION
        Wraps listStorageTags, the tags defined on primary storage pools (the tags
        disk offerings match against to place volumes). Analogous to Get-CSHostTag.

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSStorageTag
        Lists every storage tag defined across the primary storage pools.
    #>

    [CmdletBinding()]
    param(
        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    $apiParams = @{}
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
    })
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listStorageTags' -Parameters $apiParams) -Command 'listStorageTags'
}

function Get-CSStorageProvider {
    <#
    .SYNOPSIS
        Lists the available storage providers.

    .DESCRIPTION
        Wraps listStorageProviders. -Type selects primary or image (secondary)
        storage providers; the provider name is what New-CSStoragePool takes as
        -Provider.

    .PARAMETER Type
        The provider type to list: primary or image

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSStorageProvider -Type primary
        Lists the primary storage providers.
    #>

    [CmdletBinding()]
    param(
        [ValidateSet('primary', 'image')]
        [string]$Type,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    $apiParams = @{}
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Type = 'type'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
    })
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listStorageProviders' -Parameters $apiParams) -Command 'listStorageProviders'
}

function Get-CSStoragePoolObject {
    <#
    .SYNOPSIS
        Lists objects (files/directories) at a path on a storage pool.

    .DESCRIPTION
        Wraps listStoragePoolObjects, browsing the contents of a primary storage
        pool at -Path. Accepts storage pool objects on the pipeline.

    .PARAMETER Id
        The storage pool to browse. Binds from a piped pool's id.

    .PARAMETER Path
        The path within the pool to list

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSStoragePoolObject -Id $poolId -Path '/'
        Lists the objects at the root of a storage pool.

    .EXAMPLE
        Get-CSStoragePool -Name 'nfs-primary' | Get-CSStoragePoolObject -Path '/templates'
        Browses a subdirectory of a pool piped in by object.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [string]$Path,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{ id = $Id }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Path = 'path'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listStoragePoolObjects' -Parameters $apiParams) -Command 'listStoragePoolObjects'
    }
}

function New-CSStoragePool {
    <#
    .SYNOPSIS
        Creates a primary storage pool.

    .DESCRIPTION
        Wraps createStoragePool. The -Scope decides where the pool is attached:
        Cluster (needs -ClusterId and -PodId), Zone (zone-wide), or Host. -Url is the
        provider-specific location, for example nfs://server/export or
        rbd://user:secret@monitor/pool.

    .PARAMETER Name
        Name for the storage pool

    .PARAMETER Url
        The provider-specific pool URL

    .PARAMETER ZoneId
        The zone the pool belongs to

    .PARAMETER Scope
        Zone, Cluster, or Host

    .PARAMETER PodId
        The pod (required for a cluster-scoped pool)

    .PARAMETER ClusterId
        The cluster (required for a cluster-scoped pool)

    .PARAMETER Hypervisor
        The hypervisor the pool serves (for a zone-scoped pool)

    .PARAMETER Provider
        The storage provider name (see Get-CSStorageProvider)

    .PARAMETER Tags
        Storage tags, as a comma list or array

    .PARAMETER CapacityBytes
        Override the pool's usable capacity in bytes

    .PARAMETER CapacityIops
        Override the pool's usable IOPS

    .PARAMETER Managed
        Whether CloudStack manages the pool (managed storage)

    .PARAMETER Details
        Extra provider details as a hashtable

    .EXAMPLE
        New-CSStoragePool -Name 'nfs-primary' -Url 'nfs://10.0.0.5/export/primary' -ZoneId $zoneId -Scope Cluster -PodId $podId -ClusterId $clusterId
        Adds a cluster-scoped NFS primary pool.

    .EXAMPLE
        New-CSStoragePool -Name 'rbd-zone' -Url 'rbd://admin:secret@10.0.0.6/cloudstack' -ZoneId $zoneId -Scope Zone -Hypervisor KVM -Provider DefaultPrimary
        Adds a zone-wide Ceph/RBD pool for KVM.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [string]$Url,

        [Parameter(Mandatory = $true)]
        [string]$ZoneId,

        [ValidateSet('Zone', 'Cluster', 'Host')]
        [string]$Scope,

        [string]$PodId,

        [string]$ClusterId,

        [string]$Hypervisor,

        [string]$Provider,

        [string[]]$Tags,

        [long]$CapacityBytes,

        [long]$CapacityIops,

        [switch]$Managed,

        [hashtable]$Details
    )

    $apiParams = @{ name = $Name; url = $Url; zoneid = $ZoneId }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Scope = 'scope'; PodId = 'podid'; ClusterId = 'clusterid'; Hypervisor = 'hypervisor'; Provider = 'provider'
        Tags = 'tags'; CapacityBytes = 'capacitybytes'; CapacityIops = 'capacityiops'; Managed = 'managed'
    })
    Add-CSMapParameter -ApiParameters $apiParams -Name 'details' -Map $Details
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'createStoragePool' -Parameters $apiParams) -Command 'createStoragePool'
}

function Set-CSStoragePool {
    <#
    .SYNOPSIS
        Updates a primary storage pool.

    .DESCRIPTION
        Wraps updateStoragePool. Only the attributes you supply are changed. Accepts
        storage pool objects on the pipeline.

    .PARAMETER Id
        The storage pool to update. Binds from a piped pool's id.

    .PARAMETER Name
        New name

    .PARAMETER Tags
        Replacement storage tags, as a comma list or array

    .PARAMETER CapacityBytes
        New usable capacity in bytes

    .PARAMETER CapacityIops
        New usable IOPS

    .PARAMETER Enabled
        Whether the pool is enabled for allocation

    .PARAMETER Url
        New provider URL

    .EXAMPLE
        Set-CSStoragePool -Id $poolId -Tags 'ssd','tier1'
        Retags a storage pool.

    .EXAMPLE
        Get-CSStoragePool -Name 'nfs-primary' | Set-CSStoragePool -Enabled $false
        Disables a pool for new allocations.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [string]$Name,

        [string[]]$Tags,

        [long]$CapacityBytes,

        [long]$CapacityIops,

        [Nullable[bool]]$Enabled,

        [string]$Url
    )

    process {
        $apiParams = @{ id = $Id }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Name = 'name'; Tags = 'tags'; CapacityBytes = 'capacitybytes'; CapacityIops = 'capacityiops'; Url = 'url'
        })
        if ($PSBoundParameters.ContainsKey('Enabled')) { $apiParams['enabled'] = ([bool]$Enabled).ToString().ToLowerInvariant() }
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'updateStoragePool' -Parameters $apiParams) -Command 'updateStoragePool'
    }
}

function Remove-CSStoragePool {
    <#
    .SYNOPSIS
        Deletes a primary storage pool.

    .DESCRIPTION
        Wraps deleteStoragePool. The pool must be in maintenance mode (see
        Enable-CSStorageMaintenance) and hold no volumes, unless -Forced is used.
        Accepts storage pool objects on the pipeline.

    .PARAMETER Id
        The storage pool to delete. Binds from a piped pool's id.

    .PARAMETER Forced
        Force deletion even if the pool still has references

    .EXAMPLE
        Remove-CSStoragePool -Id $poolId
        Deletes a storage pool after confirmation.

    .EXAMPLE
        Get-CSStoragePool -Name 'old-nfs' | Enable-CSStorageMaintenance -Wait | Out-Null
        Get-CSStoragePool -Name 'old-nfs' | Remove-CSStoragePool
        Puts a pool into maintenance and then deletes it.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [switch]$Forced
    )

    process {
        $apiParams = @{ id = $Id }
        if ($Forced) { $apiParams['forced'] = 'true' }
        if ($PSCmdlet.ShouldProcess("storage pool $Id", 'Delete')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'deleteStoragePool' -Parameters $apiParams) -Command 'deleteStoragePool'
        }
    }
}

function Enable-CSStorageMaintenance {
    <#
    .SYNOPSIS
        Puts a primary storage pool into maintenance mode.

    .DESCRIPTION
        Wraps enableStorageMaintenance, which migrates volumes off the pool where it
        can and stops new allocations. This is an asynchronous job; use -Wait to
        block until it finishes. Accepts storage pool objects on the pipeline.

    .PARAMETER Id
        The storage pool to put into maintenance. Binds from a piped pool's id.

    .PARAMETER Wait
        Wait for the async job to finish and return the pool

    .EXAMPLE
        Enable-CSStorageMaintenance -Id $poolId -Wait
        Puts a pool into maintenance and waits for it to settle.

    .EXAMPLE
        Get-CSStoragePool -Name 'nfs-primary' | Enable-CSStorageMaintenance -Wait
        Enables maintenance on a pool piped in by object.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [switch]$Wait
    )

    process {
        if ($PSCmdlet.ShouldProcess("storage pool $Id", 'Enable maintenance')) {
            Invoke-CSAsyncApiRequest -Command 'enableStorageMaintenance' -Parameters @{ id = $Id } -Wait:$Wait
        }
    }
}

function Disable-CSStorageMaintenance {
    <#
    .SYNOPSIS
        Cancels maintenance mode on a primary storage pool.

    .DESCRIPTION
        Wraps cancelStorageMaintenance, bringing a pool back into service. This is an
        asynchronous job; use -Wait to block until it finishes. Accepts storage pool
        objects on the pipeline.

    .PARAMETER Id
        The storage pool to bring out of maintenance. Binds from a piped pool's id.

    .PARAMETER Wait
        Wait for the async job to finish and return the pool

    .EXAMPLE
        Disable-CSStorageMaintenance -Id $poolId -Wait
        Brings a pool back into service and waits for it.

    .EXAMPLE
        Get-CSStoragePool -Name 'nfs-primary' | Disable-CSStorageMaintenance -Wait
        Cancels maintenance on a pool piped in by object.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [switch]$Wait
    )

    process {
        if ($PSCmdlet.ShouldProcess("storage pool $Id", 'Cancel maintenance')) {
            Invoke-CSAsyncApiRequest -Command 'cancelStorageMaintenance' -Parameters @{ id = $Id } -Wait:$Wait
        }
    }
}

function Find-CSStoragePoolForMigration {
    <#
    .SYNOPSIS
        Lists storage pools a volume can be migrated to.

    .DESCRIPTION
        Wraps findStoragePoolsForMigration, returning the pools that are suitable
        targets for migrating a given volume (with suitability and disruption
        flags). Accepts volume objects on the pipeline.

    .PARAMETER Id
        The volume to find migration targets for. Binds from a piped volume's id.

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Find-CSStoragePoolForMigration -Id $volumeId
        Lists the pools a volume could be moved to.

    .EXAMPLE
        Get-CSVM -Name 'db-01' | Get-CSVolume -Type ROOT | Find-CSStoragePoolForMigration
        Finds migration targets for a VM's root volume.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('VolumeId')]
        [string]$Id,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{ id = $Id }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'findStoragePoolsForMigration' -Parameters $apiParams) -Command 'findStoragePoolsForMigration'
    }
}

function Sync-CSStoragePool {
    <#
    .SYNOPSIS
        Syncs a storage pool with the management server.

    .DESCRIPTION
        Wraps syncStoragePool. Currently used for a VMware datastore cluster, where
        it discovers the datastores it contains. This is an asynchronous job; use
        -Wait to block until it finishes. Accepts storage pool objects on the
        pipeline.

    .PARAMETER Id
        The storage pool to sync. Binds from a piped pool's id.

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        Sync-CSStoragePool -Id $datastoreClusterId -Wait
        Syncs a VMware datastore cluster and waits for it.

    .EXAMPLE
        Get-CSStoragePool -Name 'vmware-dscluster' | Sync-CSStoragePool -Wait
        Syncs a pool piped in by object.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [switch]$Wait
    )

    process {
        if ($PSCmdlet.ShouldProcess("storage pool $Id", 'Sync with management server')) {
            Invoke-CSAsyncApiRequest -Command 'syncStoragePool' -Parameters @{ id = $Id } -Wait:$Wait
        }
    }
}

function Sync-CSStorageCapability {
    <#
    .SYNOPSIS
        Syncs the capabilities of storage pools.

    .DESCRIPTION
        Wraps updateStorageCapabilities, refreshing CloudStack's record of what a
        primary storage pool supports. Accepts storage pool objects on the pipeline.

    .PARAMETER Id
        The storage pool whose capabilities to sync. Binds from a piped pool's id.

    .EXAMPLE
        Sync-CSStorageCapability -Id $poolId
        Refreshes the recorded capabilities of a storage pool.

    .EXAMPLE
        Get-CSStoragePool -ZoneId $zoneId | Sync-CSStorageCapability
        Syncs capabilities for every pool in a zone.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id
    )

    process {
        if ($PSCmdlet.ShouldProcess("storage pool $Id", 'Sync capabilities')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'updateStorageCapabilities' -Parameters @{ id = $Id }) -Command 'updateStorageCapabilities'
        }
    }
}

function Set-CSStoragePoolScope {
    <#
    .SYNOPSIS
        Changes the scope of a primary storage pool.

    .DESCRIPTION
        Wraps changeStoragePoolScope. The pool must be in the Disabled state.
        Narrowing from Zone to Cluster needs -ClusterId, and may require stopping or
        destroying affected VMs first (see Get-CSStorageScopeChangeAffectedVm). This
        is an asynchronous job; use -Wait to block until it finishes. Accepts storage
        pool objects on the pipeline.

    .PARAMETER Id
        The storage pool to change. Binds from a piped pool's id.

    .PARAMETER Scope
        The new scope: Zone or Cluster

    .PARAMETER ClusterId
        The target cluster, when changing to Cluster scope

    .PARAMETER Wait
        Wait for the async job to finish and return the result

    .EXAMPLE
        Set-CSStoragePoolScope -Id $poolId -Scope Cluster -ClusterId $clusterId -Wait
        Narrows a zone-wide pool to a single cluster.

    .EXAMPLE
        Get-CSStoragePool -Name 'nfs-primary' | Set-CSStoragePoolScope -Scope Zone -Wait
        Widens a cluster pool to zone scope.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [Parameter(Mandatory = $true)]
        [ValidateSet('Zone', 'Cluster')]
        [string]$Scope,

        [string]$ClusterId,

        [switch]$Wait
    )

    process {
        if ($Scope -eq 'Cluster' -and -not $PSBoundParameters.ContainsKey('ClusterId')) {
            throw '-ClusterId is required when changing to Cluster scope.'
        }
        $apiParams = @{ id = $Id; scope = $Scope }
        if ($PSBoundParameters.ContainsKey('ClusterId')) { $apiParams['clusterid'] = $ClusterId }
        if ($PSCmdlet.ShouldProcess("storage pool $Id", "Change scope to $Scope")) {
            Invoke-CSAsyncApiRequest -Command 'changeStoragePoolScope' -Parameters $apiParams -Wait:$Wait
        }
    }
}

function Get-CSStorageScopeChangeAffectedVm {
    <#
    .SYNOPSIS
        Lists the VMs affected by narrowing a storage pool's scope.

    .DESCRIPTION
        Wraps listAffectedVmsForStorageScopeChange, showing which user VMs would need
        to be stopped and which system VMs destroyed before changing a pool's scope
        from Zone to Cluster. Accepts storage pool objects on the pipeline.

    .PARAMETER Id
        The storage pool whose scope change to check. Binds from a piped pool's id.

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSStorageScopeChangeAffectedVm -Id $poolId
        Lists the VMs that a scope change would disrupt.

    .EXAMPLE
        Get-CSStoragePool -Name 'nfs-primary' | Get-CSStorageScopeChangeAffectedVm
        Checks the impact for a pool piped in by object.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('StoragePoolId', 'PoolId')]
        [string]$Id,

        [int]$Page,

        [int]$PageSize
    )

    process {
        $apiParams = @{ id = $Id }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Page = 'page'; PageSize = 'pagesize'
        })
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listAffectedVmsForStorageScopeChange' -Parameters $apiParams) -Command 'listAffectedVmsForStorageScopeChange'
    }
}