diff --git a/CHANGELOG.md b/CHANGELOG.md index fb2edce..fcc6e1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,36 @@ All notable changes to `PureStorageFlashBladePowerShell` are documented in this file. +## [2.1.2] - 2026-07-20 + +API-drift cleanup: parameter-validation fixes and removal of three cmdlets that +never worked. Surfaced by the automated API drift report (module cross-checked +against the FlashBlade OpenAPI specs, REST 2.0-2.27). + +### Removed + +- Three cmdlets that modeled REST endpoints that **never existed** in any FlashBlade + API version (2.0-2.27) and always returned HTTP 405: `Remove-PfbSession`, + `New-PfbNetworkAccessPolicy`, `Remove-PfbNetworkAccessPolicy`. FlashBlade sessions + are read-only (`puresession` is list-only) and network-access policies are a fixed + built-in set (no create/delete) - only their *rules* are mutable, which is already + covered by `New-`/`Remove-PfbNetworkAccessRule`. Because these cmdlets never + succeeded, no working script depended on them. + +### Fixed + +- `Get-PfbArrayPerformance -Protocol`: added the documented `all` value (was missing + from the ValidateSet, so a valid call was rejected client-side). +- `New-PfbAlertWatcher -MinimumSeverity`: removed the invalid `error` value (not a + valid severity; the array would reject it). +- `New-PfbQuotaUser`: fixed a test that could hang on an interactive prompt when + neither `-UserName` nor `-UserId` was supplied. + +### Added + +- `Get-PfbArraySpace -Type`: added a ValidateSet (`array`, `file-system`, + `object-store`) matching the spec. + ## [2.1.1] - 2026-07-15 Cross-platform and Windows PowerShell 5.1 fixes. diff --git a/Public/Admin/Remove-PfbSession.ps1 b/Public/Admin/Remove-PfbSession.ps1 deleted file mode 100644 index 5f170eb..0000000 --- a/Public/Admin/Remove-PfbSession.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -function Remove-PfbSession { - <# - .SYNOPSIS - Terminates an active session on the FlashBlade. - .DESCRIPTION - The Remove-PfbSession cmdlet terminates an active administrator or API session on the - connected Pure Storage FlashBlade. This is a high-impact operation because terminating - a session will immediately disconnect the associated user or application. - .PARAMETER Name - The name of the session to terminate. - .PARAMETER Id - The ID of the session to terminate. - .PARAMETER Array - The FlashBlade connection object. If not specified, the default connection is used. - .EXAMPLE - Remove-PfbSession -Name 'session-001' - - Terminates the session named 'session-001' after confirmation. - .EXAMPLE - Remove-PfbSession -Id '12345678-abcd-efgh-ijkl-123456789012' -Confirm:$false - - Terminates a session by ID without prompting for confirmation. - .EXAMPLE - Get-PfbSession -Filter "user_interface='CLI'" | Remove-PfbSession - - Terminates all CLI sessions via pipeline input. - #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] - param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, - [Parameter()] [PSCustomObject]$Array - ) - - begin { - Assert-PfbConnection -Array ([ref]$Array) - } - - process { - $target = if ($Name) { $Name } else { $Id } - $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } - if ($PSCmdlet.ShouldProcess($target, 'Terminate session')) { - Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'sessions' -QueryParams $queryParams - } - } -} diff --git a/Public/Alert/New-PfbAlertWatcher.ps1 b/Public/Alert/New-PfbAlertWatcher.ps1 index 7eef0be..17b2da4 100644 --- a/Public/Alert/New-PfbAlertWatcher.ps1 +++ b/Public/Alert/New-PfbAlertWatcher.ps1 @@ -5,7 +5,7 @@ function New-PfbAlertWatcher { .PARAMETER Name The email address of the watcher. .PARAMETER MinimumSeverity - Minimum severity level for notifications ('warning', 'info', 'error', 'critical'). + Minimum severity level for notifications ('info', 'warning', 'critical'). .PARAMETER Attributes A hashtable of additional attributes. .PARAMETER Array @@ -19,7 +19,7 @@ function New-PfbAlertWatcher { [string]$Name, [Parameter()] - [ValidateSet('info', 'warning', 'error', 'critical')] + [ValidateSet('info', 'warning', 'critical')] [string]$MinimumSeverity, [Parameter()] [hashtable]$Attributes, diff --git a/Public/Array/Get-PfbArrayPerformance.ps1 b/Public/Array/Get-PfbArrayPerformance.ps1 index f144c9c..9313383 100644 --- a/Public/Array/Get-PfbArrayPerformance.ps1 +++ b/Public/Array/Get-PfbArrayPerformance.ps1 @@ -7,7 +7,8 @@ function Get-PfbArrayPerformance { .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .PARAMETER Protocol - Filter by protocol type: 'nfs', 'smb', 'http', 's3'. + Filter by protocol type: 'all', 'nfs', 'smb', 'http', 's3'. Defaults to 'all' (combined + performance of all available protocols) when omitted. .PARAMETER Resolution Time resolution for historical data in milliseconds. .PARAMETER StartTime @@ -25,7 +26,7 @@ function Get-PfbArrayPerformance { [PSCustomObject]$Array, [Parameter()] - [ValidateSet('nfs', 'smb', 'http', 's3')] + [ValidateSet('all', 'nfs', 'smb', 'http', 's3')] [string]$Protocol, [Parameter()] diff --git a/Public/Array/Get-PfbArraySpace.ps1 b/Public/Array/Get-PfbArraySpace.ps1 index 66688c0..41972da 100644 --- a/Public/Array/Get-PfbArraySpace.ps1 +++ b/Public/Array/Get-PfbArraySpace.ps1 @@ -7,7 +7,8 @@ function Get-PfbArraySpace { .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .PARAMETER Type - Filter by space type (e.g., 'array', 'file-system', 'object-store'). + Filter by space type. Valid values: 'array', 'file-system', 'object-store'. + Defaults to 'array' if not specified. .EXAMPLE Get-PfbArraySpace #> @@ -17,6 +18,7 @@ function Get-PfbArraySpace { [PSCustomObject]$Array, [Parameter()] + [ValidateSet('array', 'file-system', 'object-store')] [string]$Type ) diff --git a/Public/Policy/New-PfbNetworkAccessPolicy.ps1 b/Public/Policy/New-PfbNetworkAccessPolicy.ps1 deleted file mode 100644 index 2891698..0000000 --- a/Public/Policy/New-PfbNetworkAccessPolicy.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -function New-PfbNetworkAccessPolicy { - <# - .SYNOPSIS - Creates a new network access policy on the FlashBlade. - .DESCRIPTION - Creates a new network access policy with the specified name and optional settings. - Use the Attributes parameter to supply a complete body hashtable, or use - individual parameters to build the request. - .PARAMETER Name - The name of the network access policy to create. - .PARAMETER Enabled - Whether the policy is enabled upon creation. - .PARAMETER Attributes - A hashtable of additional attributes for the policy body. - When specified, this is used as the entire request body. - .PARAMETER Array - The FlashBlade connection object. If not specified, the default connection is used. - .EXAMPLE - New-PfbNetworkAccessPolicy -Name "net-access-01" - - Creates a new network access policy named 'net-access-01'. - .EXAMPLE - New-PfbNetworkAccessPolicy -Name "net-access-01" -Enabled - - Creates a new enabled network access policy. - .EXAMPLE - New-PfbNetworkAccessPolicy -Name "net-access-01" -Attributes @{ enabled = $true; rules = @(@{ client = "10.0.0.0/8"; effect = "allow" }) } - - Creates a new network access policy with custom attributes. - #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] - param( - [Parameter(Mandatory, Position = 0)] - [string]$Name, - - [Parameter()] - [switch]$Enabled, - - [Parameter()] - [hashtable]$Attributes, - - [Parameter()] [PSCustomObject]$Array - ) - - Assert-PfbConnection -Array ([ref]$Array) - - if ($Attributes) { $body = $Attributes } - else { - $body = @{} - if ($PSBoundParameters.ContainsKey('Enabled')) { $body['enabled'] = [bool]$Enabled } - } - - $queryParams = @{ 'names' = $Name } - - if ($PSCmdlet.ShouldProcess($Name, 'Create network access policy')) { - Invoke-PfbApiRequest -Array $Array -Method POST -Endpoint 'network-access-policies' -Body $body -QueryParams $queryParams - } -} diff --git a/Public/Policy/Remove-PfbNetworkAccessPolicy.ps1 b/Public/Policy/Remove-PfbNetworkAccessPolicy.ps1 deleted file mode 100644 index c94b73b..0000000 --- a/Public/Policy/Remove-PfbNetworkAccessPolicy.ps1 +++ /dev/null @@ -1,53 +0,0 @@ -function Remove-PfbNetworkAccessPolicy { - <# - .SYNOPSIS - Removes a network access policy from the FlashBlade. - .DESCRIPTION - Deletes a network access policy identified by name or ID from the FlashBlade. - This action is irreversible. Use -Confirm:$false to suppress the confirmation - prompt in automation scenarios. - .PARAMETER Name - The name of the network access policy to remove. - .PARAMETER Id - The ID of the network access policy to remove. - .PARAMETER Array - The FlashBlade connection object. If not specified, the default connection is used. - .EXAMPLE - Remove-PfbNetworkAccessPolicy -Name "net-access-01" - - Removes the network access policy named 'net-access-01'. - .EXAMPLE - Remove-PfbNetworkAccessPolicy -Id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" - - Removes the network access policy by ID. - .EXAMPLE - Remove-PfbNetworkAccessPolicy -Name "net-access-01" -Confirm:$false - - Removes the network access policy without confirmation. - #> - [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] - param( - [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] - [string]$Name, - - [Parameter(ParameterSetName = 'ById', Mandatory)] - [string]$Id, - - [Parameter()] [PSCustomObject]$Array - ) - - begin { - Assert-PfbConnection -Array ([ref]$Array) - } - - process { - $target = if ($Name) { $Name } else { $Id } - $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } - - if ($PSCmdlet.ShouldProcess($target, 'Remove network access policy')) { - Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'network-access-policies' -QueryParams $queryParams - } - } -} diff --git a/PureStorageFlashBladePowerShell.psd1 b/PureStorageFlashBladePowerShell.psd1 index ca236a8..d42386e 100644 --- a/PureStorageFlashBladePowerShell.psd1 +++ b/PureStorageFlashBladePowerShell.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PureStorageFlashBladePowerShell.psm1' - ModuleVersion = '2.1.1' + ModuleVersion = '2.1.2' GUID = 'b25473b3-9eb7-414d-8da1-264e10f73d86' Author = 'Pure Storage, Inc.' CompanyName = 'Pure Storage, Inc.' @@ -260,7 +260,6 @@ 'New-PfbManagementAccessPolicy', 'New-PfbManagementAccessPolicyAdmin', 'New-PfbManagementAccessPolicyDirectoryRole', - 'New-PfbNetworkAccessPolicy', 'New-PfbNetworkAccessRule', 'New-PfbNetworkInterface', 'New-PfbNetworkInterfaceTlsPolicy', @@ -364,7 +363,6 @@ 'Remove-PfbManagementAccessPolicy', 'Remove-PfbManagementAccessPolicyAdmin', 'Remove-PfbManagementAccessPolicyDirectoryRole', - 'Remove-PfbNetworkAccessPolicy', 'Remove-PfbNetworkAccessRule', 'Remove-PfbNetworkInterface', 'Remove-PfbNetworkInterfaceTlsPolicy', @@ -402,7 +400,6 @@ 'Remove-PfbS3ExportRule', 'Remove-PfbSaml2Idp', 'Remove-PfbServer', - 'Remove-PfbSession', 'Remove-PfbSmbClientPolicy', 'Remove-PfbSmbClientRule', 'Remove-PfbSmbSharePolicy', @@ -551,6 +548,12 @@ LicenseUri = 'https://github.com/PureStorage-OpenConnect/flashblade-powershell/blob/main/LICENSE' # ReleaseNotes carries only the latest-version highlight; full history is in CHANGELOG.md. ReleaseNotes = @' +v2.1.2 - API-drift cleanup. Removes three cmdlets that modeled endpoints that never + existed in the FlashBlade API and always returned HTTP 405 (Remove-PfbSession, + New-/Remove-PfbNetworkAccessPolicy; network-access policy *rules* remain fully + supported via New-/Remove-PfbNetworkAccessRule). Also fixes parameter ValidateSets + on Get-PfbArrayPerformance -Protocol and New-PfbAlertWatcher -MinimumSeverity. + v2.1.1 - Cross-platform + Windows PowerShell 5.1 fixes. Fixes a SecureString password truncation on Linux/macOS (affected encrypted-key JWT signing and native username/password login, which silently used only the first diff --git a/Tests/Get-PfbArrayPerformance.Tests.ps1 b/Tests/Get-PfbArrayPerformance.Tests.ps1 new file mode 100644 index 0000000..3248744 --- /dev/null +++ b/Tests/Get-PfbArrayPerformance.Tests.ps1 @@ -0,0 +1,48 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + Import-Module "$PSScriptRoot/../PureStorageFlashBladePowerShell.psd1" -Force + # A throwaway connection object; Assert-PfbConnection is mocked so its contents don't matter. + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Get-PfbArrayPerformance' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'ValidateSet on -Protocol includes exactly all, nfs, smb, http, s3' { + $command = Get-Command Get-PfbArrayPerformance + $validateSetAttr = $command.Parameters['Protocol'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $validateSetAttr.ValidValues | Should -Not -BeNullOrEmpty + ($validateSetAttr.ValidValues | Sort-Object) | Should -Be (@('all', 'http', 'nfs', 's3', 'smb') | Sort-Object) + } + + It 'accepts -Protocol all (regression: was rejected before the ValidateSet fix) and passes it through' { + { Get-PfbArrayPerformance -Protocol all -Array $fakeArray } | Should -Not -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Endpoint -eq 'arrays/performance' -and $QueryParams['protocol'] -eq 'all' + } + } + + It 'still accepts pre-existing value -Protocol nfs unchanged' { + { Get-PfbArrayPerformance -Protocol nfs -Array $fakeArray } | Should -Not -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Endpoint -eq 'arrays/performance' -and $QueryParams['protocol'] -eq 'nfs' + } + } + + It 'rejects an invalid -Protocol value with zero API calls' { + { Get-PfbArrayPerformance -Protocol bogus -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'makes a call with no protocol query param when -Protocol is omitted' { + Get-PfbArrayPerformance -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Endpoint -eq 'arrays/performance' -and -not $QueryParams.ContainsKey('protocol') + } + } +} diff --git a/Tests/Get-PfbArraySpace.Tests.ps1 b/Tests/Get-PfbArraySpace.Tests.ps1 new file mode 100644 index 0000000..a7873e8 --- /dev/null +++ b/Tests/Get-PfbArraySpace.Tests.ps1 @@ -0,0 +1,39 @@ +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' } + +BeforeAll { + Import-Module "$PSScriptRoot/../PureStorageFlashBladePowerShell.psd1" -Force + $script:fakeArray = [PSCustomObject]@{ Endpoint = 'fb.example.test'; ApiVersion = '2.0'; AuthToken = 'x' } +} + +Describe 'Get-PfbArraySpace' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'restricts -Type to the three real spec-documented values' { + $attr = (Get-Command Get-PfbArraySpace).Parameters['Type'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('array', 'file-system', 'object-store') + } + + It 'rejects an invalid -Type value before making any API call' { + { Get-PfbArraySpace -Type 'bogus' -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'passes a valid -Type through to the query string' { + Get-PfbArraySpace -Type 'file-system' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'arrays/space' -and $QueryParams['type'] -eq 'file-system' + } + } + + It 'omits -Type from the query string when not specified (server defaults to array)' { + Get-PfbArraySpace -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('type') + } + } +} diff --git a/Tests/New-PfbQuotaUser.Tests.ps1 b/Tests/New-PfbQuotaUser.Tests.ps1 index 85e9d9a..14d88c9 100644 --- a/Tests/New-PfbQuotaUser.Tests.ps1 +++ b/Tests/New-PfbQuotaUser.Tests.ps1 @@ -70,9 +70,23 @@ Describe 'New-PfbQuotaUser' { } Context 'identity validation' { - It 'throws when neither -UserName nor -UserId is supplied' { - { New-PfbQuotaUser -FileSystemName 'fs-share' -Quota 100 -Confirm:$false -Array $fakeArray } | - Should -Throw + It 'declares -UserName and -UserId mandatory in mutually exclusive parameter sets (rejects neither being supplied)' { + # Deliberately does NOT invoke the cmdlet with neither -UserName nor -UserId: + # PowerShell's own parameter binder can't resolve a parameter set in that case, + # falls back to the default ('ByName'), and then PROMPTS INTERACTIVELY for the + # missing mandatory -UserName instead of letting the cmdlet's own code run — + # which hangs forever with no TTY to answer it (confirmed live, in both a real + # interactive terminal and this suite's own non-interactive runner). Asserting + # against the parameter metadata proves the same "neither supplied" case is + # rejected, without ever risking that hang. + $cmd = Get-Command New-PfbQuotaUser + $userNameSet = $cmd.Parameters['UserName'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.ParameterSetName -eq 'ByName' } + $userIdSet = $cmd.Parameters['UserId'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.ParameterSetName -eq 'ById' } + + $userNameSet.Mandatory | Should -BeTrue + $userIdSet.Mandatory | Should -BeTrue } It 'throws when both -UserName and -UserId are supplied' {