From bf7e4e3229a6e04c22d3dc2f5d5771ebc398ce9e Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:21:00 -0700 Subject: [PATCH 1/9] Add ValidateSet to Get-PfbArrayConnectionPerformanceReplication -Type The array-connections/performance/replication endpoint documents a spec-confirmed type query parameter (all/file-system/object-store) since REST 2.0, but the cmdlet never exposed it. Verified against tools/specs/ (28 cached versions, 2.0-2.27): the value list is byte-identical across every version, so a client-side ValidateSet is safe per this project's stability rule. Adds Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 (no prior coverage existed) covering the ValidateSet attribute, rejection of an invalid value with zero API calls, pass-through of a valid value, and omission of -Type from the query string when unspecified. Live-verified against FB-A (10.21.243.66): all/file-system/object-store each return real, distinct data (file-system/object-store counters selectively blank out per type); an invalid value now throws client-side before any HTTP call. --- ...bArrayConnectionPerformanceReplication.ps1 | 7 ++++ ...ConnectionPerformanceReplication.Tests.ps1 | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 diff --git a/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 b/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 index 2b156181..e83d6c8b 100644 --- a/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 +++ b/Public/Replication/Get-PfbArrayConnectionPerformanceReplication.ps1 @@ -19,6 +19,9 @@ function Get-PfbArrayConnectionPerformanceReplication { End of the time range for historical data (epoch milliseconds). .PARAMETER Resolution Time resolution for data points in milliseconds (e.g., 30000, 86400000). + .PARAMETER Type + Restricts results to replication performance for a specific object type. Valid values + are "all", "file-system", and "object-store". .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -41,6 +44,9 @@ function Get-PfbArrayConnectionPerformanceReplication { [Parameter()] [long]$StartTime, [Parameter()] [long]$EndTime, [Parameter()] [long]$Resolution, + [Parameter()] + [ValidateSet('all', 'file-system', 'object-store')] + [string]$Type, [Parameter()] [PSCustomObject]$Array ) begin { @@ -61,6 +67,7 @@ function Get-PfbArrayConnectionPerformanceReplication { if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } + if ($Type) { $queryParams['type'] = $Type } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'array-connections/performance/replication' -QueryParams $queryParams -AutoPaginate } } diff --git a/Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 b/Tests/Get-PfbArrayConnectionPerformanceReplication.Tests.ps1 new file mode 100644 index 00000000..bb88497c --- /dev/null +++ b/Tests/Get-PfbArrayConnectionPerformanceReplication.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-PfbArrayConnectionPerformanceReplication' { + 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-PfbArrayConnectionPerformanceReplication).Parameters['Type'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('all', 'file-system', 'object-store') + } + + It 'rejects an invalid -Type value before making any API call' { + { Get-PfbArrayConnectionPerformanceReplication -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-PfbArrayConnectionPerformanceReplication -Type 'file-system' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'array-connections/performance/replication' -and $QueryParams['type'] -eq 'file-system' + } + } + + It 'omits -Type from the query string when not specified' { + Get-PfbArrayConnectionPerformanceReplication -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('type') + } + } +} From b503b3791605db2b288c9e845a1ea0b07836e22c Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:21:19 -0700 Subject: [PATCH 2/9] Add ValidateSet to Get-PfbArrayPerformanceReplication -Type The arrays/performance/replication endpoint documents a spec-confirmed type query parameter (all/file-system/object-store) since REST 2.0 (shares the Type_for_performance component with array-connections/performance/replication from spec v2.17 onward), but the cmdlet never exposed it. Verified against tools/specs/: byte-identical value list across every version, so a client-side ValidateSet is safe. Adds Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 (no prior coverage existed) covering the ValidateSet attribute, rejection of an invalid value with zero API calls, pass-through of a valid value, and omission of -Type when unspecified. Live-verified against FB-A (10.21.243.66): all/file-system/object-store each return real, distinct data; an invalid value now throws client-side before any HTTP call. --- .../Get-PfbArrayPerformanceReplication.ps1 | 7 ++++ ...t-PfbArrayPerformanceReplication.Tests.ps1 | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 diff --git a/Public/Array/Get-PfbArrayPerformanceReplication.ps1 b/Public/Array/Get-PfbArrayPerformanceReplication.ps1 index 0153eec2..e062757a 100644 --- a/Public/Array/Get-PfbArrayPerformanceReplication.ps1 +++ b/Public/Array/Get-PfbArrayPerformanceReplication.ps1 @@ -17,6 +17,9 @@ function Get-PfbArrayPerformanceReplication { End of the time range for historical data (epoch milliseconds or datetime string). .PARAMETER Resolution Time resolution for data points in milliseconds (e.g. 30000, 86400000). + .PARAMETER Type + Restricts results to replication performance for a specific object type. Valid values + are "all", "file-system", and "object-store". .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -37,6 +40,9 @@ function Get-PfbArrayPerformanceReplication { [Parameter()] [long]$StartTime, [Parameter()] [long]$EndTime, [Parameter()] [long]$Resolution, + [Parameter()] + [ValidateSet('all', 'file-system', 'object-store')] + [string]$Type, [Parameter()] [PSCustomObject]$Array ) @@ -52,6 +58,7 @@ function Get-PfbArrayPerformanceReplication { if ($StartTime) { $queryParams['start_time'] = $StartTime } if ($EndTime) { $queryParams['end_time'] = $EndTime } if ($Resolution) { $queryParams['resolution'] = $Resolution } + if ($Type) { $queryParams['type'] = $Type } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'arrays/performance/replication' -QueryParams $queryParams -AutoPaginate } diff --git a/Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 b/Tests/Get-PfbArrayPerformanceReplication.Tests.ps1 new file mode 100644 index 00000000..494a33d0 --- /dev/null +++ b/Tests/Get-PfbArrayPerformanceReplication.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-PfbArrayPerformanceReplication' { + 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-PfbArrayPerformanceReplication).Parameters['Type'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('all', 'file-system', 'object-store') + } + + It 'rejects an invalid -Type value before making any API call' { + { Get-PfbArrayPerformanceReplication -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-PfbArrayPerformanceReplication -Type 'file-system' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'arrays/performance/replication' -and $QueryParams['type'] -eq 'file-system' + } + } + + It 'omits -Type from the query string when not specified' { + Get-PfbArrayPerformanceReplication -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('type') + } + } +} From a3b3dd3bbe0cd16df8ceb251aecdcfa66fe97a45 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:21:45 -0700 Subject: [PATCH 3/9] Add ValidateSet to Test-PfbSupport -TestType The support/test endpoint documents a spec-confirmed test_type query parameter (all/phonehome/remote-assist) since REST 2.0, but the cmdlet never exposed it or built any query string at all. Verified against tools/specs/: value tokens are byte-identical across every version (prose wording changed cosmetically at v2.17's component refactor, but the values themselves never did), so a client-side ValidateSet is safe. Adds Tests/Test-PfbSupport.Tests.ps1 (no prior coverage existed) covering the ValidateSet attribute, rejection of an invalid value with zero API calls, pass-through of a valid value, and omission of -TestType when unspecified. Live-verified against FB-A (10.21.243.66): phonehome and remote-assist (with an extended HTTP timeout, since this isolated lab has no real phonehome/ remote-assist connectivity and the default 30s timeout was too short) both return real, distinct test_type results; an invalid value now throws client-side before any HTTP call. --- Public/Support/Test-PfbSupport.ps1 | 20 +++++++++++++-- Tests/Test-PfbSupport.Tests.ps1 | 39 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 Tests/Test-PfbSupport.Tests.ps1 diff --git a/Public/Support/Test-PfbSupport.ps1 b/Public/Support/Test-PfbSupport.ps1 index 9cfeb9e0..6fb7c3a0 100644 --- a/Public/Support/Test-PfbSupport.ps1 +++ b/Public/Support/Test-PfbSupport.ps1 @@ -6,12 +6,19 @@ function Test-PfbSupport { The Test-PfbSupport cmdlet tests the support connectivity of the connected Pure Storage FlashBlade. This returns test results indicating whether Phone Home and remote assist connections are functioning properly. + .PARAMETER TestType + Restricts which support connectivity test is run. Valid values are "all", "phonehome", + and "remote-assist". .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE Test-PfbSupport Runs the support connectivity test on the connected FlashBlade. + .EXAMPLE + Test-PfbSupport -TestType 'phonehome' + + Runs only the phone-home connectivity test. .EXAMPLE Test-PfbSupport -Array $FlashBlade @@ -22,7 +29,16 @@ function Test-PfbSupport { Runs the support test and displays the test type from the results. #> [CmdletBinding()] - param([Parameter()] [PSCustomObject]$Array) + param( + [Parameter()] + [ValidateSet('all', 'phonehome', 'remote-assist')] + [string]$TestType, + + [Parameter()] + [PSCustomObject]$Array + ) Assert-PfbConnection -Array ([ref]$Array) - Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'support/test' + $queryParams = @{} + if ($TestType) { $queryParams['test_type'] = $TestType } + Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'support/test' -QueryParams $queryParams } diff --git a/Tests/Test-PfbSupport.Tests.ps1 b/Tests/Test-PfbSupport.Tests.ps1 new file mode 100644 index 00000000..7f114867 --- /dev/null +++ b/Tests/Test-PfbSupport.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 'Test-PfbSupport' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'restricts -TestType to the three real spec-documented values' { + $attr = (Get-Command Test-PfbSupport).Parameters['TestType'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('all', 'phonehome', 'remote-assist') + } + + It 'rejects an invalid -TestType value before making any API call' { + { Test-PfbSupport -TestType 'bogus' -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'passes a valid -TestType through to the query string' { + Test-PfbSupport -TestType 'phonehome' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'support/test' -and $QueryParams['test_type'] -eq 'phonehome' + } + } + + It 'omits -TestType from the query string when not specified' { + Test-PfbSupport -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('test_type') + } + } +} From 8443ce94d1e9beead2220d3e08c3d96a74051230 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:22:11 -0700 Subject: [PATCH 4/9] Add ArgumentCompleter to Get-PfbPolicyAllMember -MemberType The policies-all/members endpoint documents a spec query parameter member_types, but the cmdlet never exposed it. Unlike the other 6 gaps in this batch, verifying tools/specs/ found the value set is NOT stable: it was 4 values (file-systems, file-system-snapshots, file-system-replica-links, object-store-users) from v2.2-v2.16, then grew to 5 at v2.17 adding object-store-accounts, and the spec prose itself uses non-exhaustive "include"/"different endpoints may accept different subsets" wording. Per this project's own established rule (a value set that changed since introduction gets an ArgumentCompleter, not a ValidateSet, even with full history -- see Value-Enum-Extraction-Work.md and the Pester fixture in Build-PfbFieldCmdletMap.Tests.ps1), -MemberType gets a tab-completing ArgumentCompleter offering all 5 known values instead, with no client-side rejection of anything outside that list. This is the first ArgumentCompleter shipped on a Public cmdlet in this module (a prior, larger, version-aware ArgumentCompleter design was evaluated and shelved for a different purpose -- hiding parameter names by array capability, which completers can't do; this one only completes a parameter's value, the case completers are actually built for). Adds Tests/Get-PfbPolicyAllMember.Tests.ps1 (no prior coverage existed) covering the completer offering all 5 values, a value outside the list NOT being rejected, pass-through of valid values comma-joined, and omission of -MemberType when unspecified. Live-verified against FB-A (10.21.243.66): all 5 known values are accepted by the server without error (including object-store-accounts, the value a 4-value ValidateSet would have permanently blocked); a genuinely bogus value is rejected server-side with a clear "Invalid member type" error -- directly confirming the ArgumentCompleter choice over ValidateSet was correct here. --- Public/Policy/Get-PfbPolicyAllMember.ps1 | 18 +++++++++ Tests/Get-PfbPolicyAllMember.Tests.ps1 | 50 ++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 Tests/Get-PfbPolicyAllMember.Tests.ps1 diff --git a/Public/Policy/Get-PfbPolicyAllMember.ps1 b/Public/Policy/Get-PfbPolicyAllMember.ps1 index cafb7eb1..d2d872c2 100644 --- a/Public/Policy/Get-PfbPolicyAllMember.ps1 +++ b/Public/Policy/Get-PfbPolicyAllMember.ps1 @@ -13,6 +13,11 @@ function Get-PfbPolicyAllMember { One or more member names to filter by. .PARAMETER MemberId One or more member IDs to filter by. + .PARAMETER MemberType + One or more member types to filter by (e.g. "file-systems", "object-store-users"). + Tab-completes the values documented as of this module's release, but the server's + accepted set has grown across REST versions and may include newer values not offered + here — any value is passed through as-is, not validated client-side. .PARAMETER Filter A server-side filter expression to narrow results. .PARAMETER Limit @@ -38,6 +43,18 @@ function Get-PfbPolicyAllMember { [Parameter()] [string[]]$PolicyId, [Parameter()] [string[]]$MemberName, [Parameter()] [string[]]$MemberId, + [Parameter()] + [ArgumentCompleter({ + param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters) + @( + 'file-systems', + 'file-system-snapshots', + 'file-system-replica-links', + 'object-store-users', + 'object-store-accounts' + ) | Where-Object { $_ -like "$WordToComplete*" } + })] + [string[]]$MemberType, [Parameter()] [string]$Filter, [Parameter()] [int]$Limit, [Parameter()] [PSCustomObject]$Array ) @@ -49,6 +66,7 @@ function Get-PfbPolicyAllMember { if ($PolicyId) { $queryParams['policy_ids'] = $PolicyId -join ',' } if ($MemberName) { $queryParams['member_names'] = $MemberName -join ',' } if ($MemberId) { $queryParams['member_ids'] = $MemberId -join ',' } + if ($MemberType) { $queryParams['member_types'] = $MemberType -join ',' } if ($Filter) { $queryParams['filter'] = $Filter } if ($Limit -gt 0) { $queryParams['limit'] = $Limit } diff --git a/Tests/Get-PfbPolicyAllMember.Tests.ps1 b/Tests/Get-PfbPolicyAllMember.Tests.ps1 new file mode 100644 index 00000000..a421935c --- /dev/null +++ b/Tests/Get-PfbPolicyAllMember.Tests.ps1 @@ -0,0 +1,50 @@ +#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-PfbPolicyAllMember' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It '-MemberType offers tab-completion for all five known spec-documented values (not a hard ValidateSet, since the spec value set has grown over time)' { + $attr = (Get-Command Get-PfbPolicyAllMember).Parameters['MemberType'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ArgumentCompleterAttribute] } + $attr | Should -Not -BeNullOrEmpty + + $validateSetAttr = (Get-Command Get-PfbPolicyAllMember).Parameters['MemberType'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $validateSetAttr | Should -BeNullOrEmpty + + $completions = & $attr.ScriptBlock 'Get-PfbPolicyAllMember' 'MemberType' '' $null @{} + ($completions | Sort-Object) | Should -Be (@( + 'file-systems', 'file-system-snapshots', 'file-system-replica-links', + 'object-store-users', 'object-store-accounts' + ) | Sort-Object) + } + + It 'does NOT reject a value outside the known completion list (non-exhaustive, no hard validation)' { + { Get-PfbPolicyAllMember -MemberType 'some-future-member-type' -Array $fakeArray } | Should -Not -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['member_types'] -eq 'some-future-member-type' + } + } + + It 'passes valid -MemberType values through to the query string, comma-joined' { + Get-PfbPolicyAllMember -MemberType 'file-systems', 'object-store-accounts' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'policies-all/members' -and $QueryParams['member_types'] -eq 'file-systems,object-store-accounts' + } + } + + It 'omits -MemberType from the query string when not specified' { + Get-PfbPolicyAllMember -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('member_types') + } + } +} From 66171756e3137414a9b37ad27a8f30102347a541 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:22:25 -0700 Subject: [PATCH 5/9] Add ValidateSet to Invoke-PfbNetworkTrace -Method The network-interfaces/trace endpoint documents a spec-confirmed method query parameter (icmp/tcp/udp) since REST 2.6, but the cmdlet never exposed it. Verified against tools/specs/: byte-identical value list across every version it appears in, so a client-side ValidateSet is safe. (The spec also documents fragment_packet, discover_mtu, component_name, port, and resolve_hostname on this endpoint -- out of scope here, this task covers only the method value-enum.) Adds Tests/Invoke-PfbNetworkTrace.Tests.ps1 (no prior coverage existed) covering the ValidateSet attribute, rejection of an invalid value with zero API calls, pass-through of a valid value, and omission of -Method when unspecified. Live-verified against FB-A (10.21.243.66): icmp/tcp/udp each return real, distinct traceroute output (icmp resolves the full path; tcp/udp show the expected blocked hops downstream); an invalid value now throws client-side before any HTTP call. --- Public/Network/Invoke-PfbNetworkTrace.ps1 | 6 ++++ Tests/Invoke-PfbNetworkTrace.Tests.ps1 | 39 +++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 Tests/Invoke-PfbNetworkTrace.Tests.ps1 diff --git a/Public/Network/Invoke-PfbNetworkTrace.ps1 b/Public/Network/Invoke-PfbNetworkTrace.ps1 index 3abdedf3..aaec1640 100644 --- a/Public/Network/Invoke-PfbNetworkTrace.ps1 +++ b/Public/Network/Invoke-PfbNetworkTrace.ps1 @@ -10,6 +10,8 @@ function Invoke-PfbNetworkTrace { The hostname or IP address to trace the route to. This parameter is mandatory. .PARAMETER SourceName The name of the network interface to use as the source of the trace. + .PARAMETER Method + The trace protocol to use. Valid values are "icmp", "tcp", and "udp". .PARAMETER Array The FlashBlade connection object. If not specified, the default connection is used. .EXAMPLE @@ -29,12 +31,16 @@ function Invoke-PfbNetworkTrace { param( [Parameter(Mandatory, Position = 0)] [string]$Destination, [Parameter()] [string]$SourceName, + [Parameter()] + [ValidateSet('icmp', 'tcp', 'udp')] + [string]$Method, [Parameter()] [PSCustomObject]$Array ) begin { Assert-PfbConnection -Array ([ref]$Array) } process { $queryParams = @{ 'destination' = $Destination } if ($SourceName) { $queryParams['source.name'] = $SourceName } + if ($Method) { $queryParams['method'] = $Method } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'network-interfaces/trace' -QueryParams $queryParams } } diff --git a/Tests/Invoke-PfbNetworkTrace.Tests.ps1 b/Tests/Invoke-PfbNetworkTrace.Tests.ps1 new file mode 100644 index 00000000..64c5ae68 --- /dev/null +++ b/Tests/Invoke-PfbNetworkTrace.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 'Invoke-PfbNetworkTrace' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'restricts -Method to the three real spec-documented values' { + $attr = (Get-Command Invoke-PfbNetworkTrace).Parameters['Method'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('icmp', 'tcp', 'udp') + } + + It 'rejects an invalid -Method value before making any API call' { + { Invoke-PfbNetworkTrace -Destination '10.0.0.1' -Method 'bogus' -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'passes a valid -Method through to the query string' { + Invoke-PfbNetworkTrace -Destination '10.0.0.1' -Method 'tcp' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'network-interfaces/trace' -and $QueryParams['method'] -eq 'tcp' + } + } + + It 'omits -Method from the query string when not specified' { + Invoke-PfbNetworkTrace -Destination '10.0.0.1' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('method') + } + } +} From a6769fef61c65898f1a7bc141718582f1ec09428 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:22:48 -0700 Subject: [PATCH 6/9] Add ValidateSet to Get-PfbFileSystemSession -Protocol The file-systems/sessions endpoint documents a spec-confirmed protocols query parameter (nfs/smb) since REST 2.10, but the cmdlet never exposed it. Verified against tools/specs/: byte-identical value tokens across every version, so a client-side ValidateSet is safe despite the spec's non- exhaustive "include" wording (tokens themselves never changed). Adds Tests/Get-PfbFileSystemSession.Tests.ps1 (no prior coverage existed) covering the ValidateSet attribute, rejection of an invalid value with zero API calls, pass-through of valid values comma-joined, and omission of -Protocol when unspecified. Live-verified against FB-A (10.21.243.66): -Protocol nfs and -Protocol smb individually both succeed (0 active sessions in this lab, as expected); an invalid value throws client-side before any HTTP call. Also discovered (sanity note, not a client bug -- confirmed via a raw Invoke-PfbApiRequest call bypassing this cmdlet entirely) that FB-A''s server rejects the comma-joined multi-value form protocols=nfs,smb with "Cannot process more than one name at a time", even with no -Name/-Id supplied -- an apparent server-side limitation on this array/Purity build despite the spec documenting protocols as a comma-separated list. --- .../FileSystem/Get-PfbFileSystemSession.ps1 | 8 ++++ Tests/Get-PfbFileSystemSession.Tests.ps1 | 39 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Tests/Get-PfbFileSystemSession.Tests.ps1 diff --git a/Public/FileSystem/Get-PfbFileSystemSession.ps1 b/Public/FileSystem/Get-PfbFileSystemSession.ps1 index 2926679b..ff4dfb3d 100644 --- a/Public/FileSystem/Get-PfbFileSystemSession.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemSession.ps1 @@ -18,6 +18,9 @@ function Get-PfbFileSystemSession { Maximum number of items to return. .PARAMETER TotalOnly Return only the total count, not the items. + .PARAMETER Protocol + Restricts results to sessions using one or more specific protocols. Valid values are + "nfs" and "smb". .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE @@ -53,6 +56,10 @@ function Get-PfbFileSystemSession { [Parameter()] [switch]$TotalOnly, + [Parameter()] + [ValidateSet('nfs', 'smb')] + [string[]]$Protocol, + [Parameter()] [PSCustomObject]$Array ) @@ -76,6 +83,7 @@ function Get-PfbFileSystemSession { if ($Sort) { $queryParams['sort'] = $Sort } if ($Limit -gt 0) { $queryParams['limit'] = $Limit } if ($TotalOnly) { $queryParams['total_only'] = 'true' } + if ($Protocol) { $queryParams['protocols'] = $Protocol -join ',' } Invoke-PfbApiRequest -Array $Array -Method GET -Endpoint 'file-systems/sessions' -QueryParams $queryParams -AutoPaginate } diff --git a/Tests/Get-PfbFileSystemSession.Tests.ps1 b/Tests/Get-PfbFileSystemSession.Tests.ps1 new file mode 100644 index 00000000..cc429cc4 --- /dev/null +++ b/Tests/Get-PfbFileSystemSession.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-PfbFileSystemSession' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'restricts -Protocol to the two real spec-documented values' { + $attr = (Get-Command Get-PfbFileSystemSession).Parameters['Protocol'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('nfs', 'smb') + } + + It 'rejects an invalid -Protocol value before making any API call' { + { Get-PfbFileSystemSession -Protocol 'bogus' -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'passes valid -Protocol values through to the query string, comma-joined' { + Get-PfbFileSystemSession -Protocol 'nfs', 'smb' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'GET' -and $Endpoint -eq 'file-systems/sessions' -and $QueryParams['protocols'] -eq 'nfs,smb' + } + } + + It 'omits -Protocol from the query string when not specified' { + Get-PfbFileSystemSession -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('protocols') + } + } +} From bfcdef91f09dc70807c79bc1472044d6de3350d1 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 11:23:17 -0700 Subject: [PATCH 7/9] Add ValidateSet to Remove-PfbFileSystemSession -Protocol The DELETE file-systems/sessions endpoint documents a spec-confirmed protocols query parameter (nfs/smb) since REST 2.10 -- the same field as Get-PfbFileSystemSession''s GET side, re-verified separately here since a query parameter existing on GET doesn''t guarantee it exists identically on DELETE (it does: same tokens, distinct Protocols_required shared component from spec v2.17). Verified byte-identical value tokens across every version, so a client-side ValidateSet is safe. Design choice: this cmdlet only ever supported single-target termination via mandatory, mutually-exclusive -Name/-Id. The real endpoint supports filtering independently of names/ids (server can bulk-terminate by protocol alone), but adding that bulk/filter-only mode would be a materially bigger, riskier change to a SupportsShouldProcess/ConfirmImpact=High destructive cmdlet. Added -Protocol as an additional, optional, set-agnostic narrowing filter instead (alongside the existing -Array parameter''s precedent) -- -Name or -Id is still required; -Protocol only narrows an already-selected target. Adds Tests/Remove-PfbFileSystemSession.Tests.ps1 (no prior coverage existed) covering the ValidateSet attribute, rejection of an invalid value with zero API calls, the still-mandatory -Name/-Id requirement even with -Protocol supplied, pass-through of valid values comma-joined alongside both -Name and -Id, omission of -Protocol when unspecified, and -WhatIf making zero calls. Not live-tested against FB-A -- this is a disruptive (ConfirmImpact = ''High'') session-termination operation; per the task instructions, mocked coverage is the verification bar here, and a live test against a real target session requires the user''s direct involvement with a genuinely disposable session. --- .../Remove-PfbFileSystemSession.ps1 | 13 ++++- Tests/Remove-PfbFileSystemSession.Tests.ps1 | 58 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Tests/Remove-PfbFileSystemSession.Tests.ps1 diff --git a/Public/FileSystem/Remove-PfbFileSystemSession.ps1 b/Public/FileSystem/Remove-PfbFileSystemSession.ps1 index 064a98bf..d6a880ee 100644 --- a/Public/FileSystem/Remove-PfbFileSystemSession.ps1 +++ b/Public/FileSystem/Remove-PfbFileSystemSession.ps1 @@ -10,6 +10,10 @@ function Remove-PfbFileSystemSession { The name of the file system whose session should be terminated. .PARAMETER Id The ID of the session to terminate. + .PARAMETER Protocol + Narrows termination to sessions using one or more specific protocols, in addition to + the required -Name or -Id target. Valid values are "nfs" and "smb". Does not replace + -Name/-Id as the target selector. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE @@ -30,6 +34,10 @@ function Remove-PfbFileSystemSession { [Parameter(ParameterSetName = 'ById', Mandatory)] [string]$Id, + [Parameter()] + [ValidateSet('nfs', 'smb')] + [string[]]$Protocol, + [Parameter()] [PSCustomObject]$Array ) @@ -40,8 +48,9 @@ function Remove-PfbFileSystemSession { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } + if ($Name) { $queryParams['names'] = $Name } + if ($Id) { $queryParams['ids'] = $Id } + if ($Protocol) { $queryParams['protocols'] = $Protocol -join ',' } $target = if ($Name) { $Name } else { $Id } diff --git a/Tests/Remove-PfbFileSystemSession.Tests.ps1 b/Tests/Remove-PfbFileSystemSession.Tests.ps1 new file mode 100644 index 00000000..d8294816 --- /dev/null +++ b/Tests/Remove-PfbFileSystemSession.Tests.ps1 @@ -0,0 +1,58 @@ +#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 'Remove-PfbFileSystemSession' { + BeforeEach { + Mock -ModuleName PureStorageFlashBladePowerShell Assert-PfbConnection { } + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } + } + + It 'restricts -Protocol to the two real spec-documented values' { + $attr = (Get-Command Remove-PfbFileSystemSession).Parameters['Protocol'].Attributes | + Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } + $attr | Should -Not -BeNullOrEmpty + $attr.ValidValues | Should -Be @('nfs', 'smb') + } + + It 'rejects an invalid -Protocol value before making any API call' { + { Remove-PfbFileSystemSession -Name 'fs01' -Protocol 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'still requires -Name or -Id even with -Protocol supplied' { + { Remove-PfbFileSystemSession -Protocol 'nfs' -Confirm:$false -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'passes valid -Protocol values through to the query string, comma-joined, alongside -Name' { + Remove-PfbFileSystemSession -Name 'fs01' -Protocol 'nfs', 'smb' -Confirm:$false -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and $Endpoint -eq 'file-systems/sessions' -and + $QueryParams['names'] -eq 'fs01' -and $QueryParams['protocols'] -eq 'nfs,smb' + } + } + + It 'passes valid -Protocol values through to the query string alongside -Id' { + Remove-PfbFileSystemSession -Id 'abc-123' -Protocol 'smb' -Confirm:$false -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $Method -eq 'DELETE' -and $Endpoint -eq 'file-systems/sessions' -and + $QueryParams['ids'] -eq 'abc-123' -and $QueryParams['protocols'] -eq 'smb' + } + } + + It 'omits -Protocol from the query string when not specified' { + Remove-PfbFileSystemSession -Name 'fs01' -Confirm:$false -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + -not $QueryParams.ContainsKey('protocols') + } + } + + It 'honors -WhatIf (no call made)' { + Remove-PfbFileSystemSession -Name 'fs01' -Protocol 'nfs' -WhatIf -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } +} From e96e4169509d01b7355d50d9613b70645f8fc99d Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 13:37:39 -0700 Subject: [PATCH 8/9] Fix Remove-PfbFileSystemSession: true -Protocol mutual exclusivity + remove fake -Id Live-testing the -Protocol addition from the prior commit against a real array (sn1-s200-c09-33.fsa.lab) surfaced two real, pre-existing bugs in this cmdlet, neither introduced by that commit: 1. The server rejects combining `names` with any other query parameter, including `protocols` -- confirmed live (400: "The passed arguments are mutually exclusive") and in the spec''s own DELETE description, stable since v2.10: "When `names` is specified, no other query parameter can be specified." The prior commit''s design (-Protocol as an *additional* narrowing filter alongside -Name) can never work against a real array. -Protocol is now its own mutually-exclusive ByProtocol parameter set: a genuine bulk -terminate-by-protocol mode, matching what the server actually supports (confirmed live: sending protocols+disruptive=true with no name succeeds). 2. There is no `ids` query parameter on this endpoint in ANY spec version (2.10-2.27 checked) -- -Id was never functional; no session object returned by this API has an "id" field at all, only a self-contained "name" (e.g. "22517998136858346-smb"). Removed entirely rather than left as permanently-broken surface. 3. -Name''s own help text was wrong: it claimed to filter by "the name of the file system," but live-verified the server rejects a file system name here ("Invalid name specified") -- it filters by the session''s own generated name, as returned by Get-PfbFileSystemSession''s Name property. Fixed. Because -Protocol is now confirmed array-wide and cross-client (no per-client/per-user filter is exposed by this cmdlet), it also requires an explicit -Force switch, checked independent of $ConfirmPreference/-Confirm -- so lowering a session''s confirm preference globally still cannot trigger a bulk purge without deliberate opt-in. The existing SupportsShouldProcess/ConfirmImpact=High prompt remains in place as an additional, separate gate. Tests/Remove-PfbFileSystemSession.Tests.ps1 rewritten: asserts -Id no longer exists, true parameter-set exclusivity between -Name/-Protocol, the required disruptive=true flag on the bulk path, the independent -Force gate, and -WhatIf on both paths. Live-verified against sn1-s200-c09-33.fsa.lab: terminated a real SMB session via the corrected -Name path (200 OK, session confirmed gone from a follow-up Get-PfbFileSystemSession call). Did not live-test the -Protocol bulk path -- this is a shared lab array with other real users'' active sessions, and -Protocol has no per-client filter to scope a live test safely. --- .../Remove-PfbFileSystemSession.ps1 | 79 +++++++++++++------ Tests/Remove-PfbFileSystemSession.Tests.ps1 | 41 ++++++---- 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/Public/FileSystem/Remove-PfbFileSystemSession.ps1 b/Public/FileSystem/Remove-PfbFileSystemSession.ps1 index d6a880ee..a727af1d 100644 --- a/Public/FileSystem/Remove-PfbFileSystemSession.ps1 +++ b/Public/FileSystem/Remove-PfbFileSystemSession.ps1 @@ -1,29 +1,50 @@ function Remove-PfbFileSystemSession { <# .SYNOPSIS - Terminates a file system session on the FlashBlade. + Terminates one or more file system sessions on the FlashBlade. .DESCRIPTION - Forces the termination of an active client session connected to a file system. - This is a disruptive operation that disconnects the client and may cause - in-progress operations to fail. + Forces the termination of active client session(s). This is a disruptive operation + that disconnects the client(s) and may cause in-progress operations to fail. + + Two mutually exclusive modes are supported, matching the real REST endpoint's own + rules (live-confirmed: the server rejects combining `names` with any other query + parameter, including `protocols`): + - -Name: terminates one specific session, identified by the session's own generated + name (as returned in the Name property of Get-PfbFileSystemSession's output) -- + NOT a file system name. + - -Protocol: bulk-terminates every active session using the given protocol(s), + across the entire array, regardless of file system or client. Cannot be combined + with -Name. The server requires an internal "disruptive" flag for this + single-filter bulk mode, which this cmdlet sets automatically -- there is no + narrower per-client/per-user filter exposed here, so any use of -Protocol affects + every matching session array-wide. Because of that blast radius, -Protocol also + requires -Force -- independent of $ConfirmPreference/-Confirm, so a caller who has + globally lowered their confirm preference still cannot trigger a bulk purge without + explicitly opting in. .PARAMETER Name - The name of the file system whose session should be terminated. - .PARAMETER Id - The ID of the session to terminate. + The session's own generated name to terminate (as returned by + Get-PfbFileSystemSession's Name property) -- NOT the name of a file system. + Mandatory in the ByName parameter set; cannot be combined with -Protocol. .PARAMETER Protocol - Narrows termination to sessions using one or more specific protocols, in addition to - the required -Name or -Id target. Valid values are "nfs" and "smb". Does not replace - -Name/-Id as the target selector. + Bulk-terminates every active session using one or more specific protocols, across + the entire array. Valid values are "nfs" and "smb". Cannot be combined with -Name -- + the server rejects that combination. This is an array-wide, cross-client operation; + there is no narrower filter exposed by this cmdlet. Requires -Force. + .PARAMETER Force + Required alongside -Protocol to acknowledge the array-wide, cross-client blast radius + of that bulk-terminate mode. This check is independent of $ConfirmPreference/-Confirm + -- it cannot be bypassed by lowering the session's confirm preference, unlike the + standard SupportsShouldProcess prompt this cmdlet also honors. .PARAMETER Array The FlashBlade connection object. If not specified, uses the default connection. .EXAMPLE - Remove-PfbFileSystemSession -Name "fs01" - Terminates sessions on file system 'fs01'. + Remove-PfbFileSystemSession -Name "22517998136858346-smb" + Terminates the single session with that session name. .EXAMPLE - Remove-PfbFileSystemSession -Id "abc-123" - Terminates the session with the specified ID. + Remove-PfbFileSystemSession -Protocol 'smb' -Force -Confirm:$false + Bulk-terminates every active SMB session on the array. .EXAMPLE - Remove-PfbFileSystemSession -Name "fs01" -Confirm:$false + Remove-PfbFileSystemSession -Name "22517998136858346-smb" -Confirm:$false Terminates the session without prompting for confirmation. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] @@ -31,13 +52,13 @@ function Remove-PfbFileSystemSession { [Parameter(ParameterSetName = 'ByName', Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [string]$Name, - [Parameter(ParameterSetName = 'ById', Mandatory)] - [string]$Id, - - [Parameter()] + [Parameter(ParameterSetName = 'ByProtocol', Mandatory)] [ValidateSet('nfs', 'smb')] [string[]]$Protocol, + [Parameter()] + [switch]$Force, + [Parameter()] [PSCustomObject]$Array ) @@ -48,13 +69,23 @@ function Remove-PfbFileSystemSession { process { $queryParams = @{} - if ($Name) { $queryParams['names'] = $Name } - if ($Id) { $queryParams['ids'] = $Id } - if ($Protocol) { $queryParams['protocols'] = $Protocol -join ',' } + $action = 'Terminate file system session' - $target = if ($Name) { $Name } else { $Id } + if ($PSCmdlet.ParameterSetName -eq 'ByProtocol') { + if (-not $Force) { + throw "Remove-PfbFileSystemSession -Protocol bulk-terminates EVERY active session using that protocol, across the entire array, for every client -- pass -Force to acknowledge this before it will run (independent of `$ConfirmPreference/-Confirm)." + } + $queryParams['protocols'] = $Protocol -join ',' + $queryParams['disruptive'] = 'true' + $target = $Protocol -join ', ' + $action = 'Bulk-terminate ALL file system sessions using protocol(s)' + } + else { + $queryParams['names'] = $Name + $target = $Name + } - if ($PSCmdlet.ShouldProcess($target, 'Terminate file system session')) { + if ($PSCmdlet.ShouldProcess($target, $action)) { Invoke-PfbApiRequest -Array $Array -Method DELETE -Endpoint 'file-systems/sessions' -QueryParams $queryParams } } diff --git a/Tests/Remove-PfbFileSystemSession.Tests.ps1 b/Tests/Remove-PfbFileSystemSession.Tests.ps1 index d8294816..7aeb6c04 100644 --- a/Tests/Remove-PfbFileSystemSession.Tests.ps1 +++ b/Tests/Remove-PfbFileSystemSession.Tests.ps1 @@ -11,6 +11,10 @@ Describe 'Remove-PfbFileSystemSession' { Mock -ModuleName PureStorageFlashBladePowerShell Invoke-PfbApiRequest { } } + It 'has no -Id parameter (the endpoint has no ids query parameter in any spec version)' { + (Get-Command Remove-PfbFileSystemSession).Parameters.ContainsKey('Id') | Should -BeFalse + } + It 'restricts -Protocol to the two real spec-documented values' { $attr = (Get-Command Remove-PfbFileSystemSession).Parameters['Protocol'].Attributes | Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] } @@ -19,40 +23,45 @@ Describe 'Remove-PfbFileSystemSession' { } It 'rejects an invalid -Protocol value before making any API call' { - { Remove-PfbFileSystemSession -Name 'fs01' -Protocol 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw + { Remove-PfbFileSystemSession -Protocol 'bogus' -Confirm:$false -Array $fakeArray } | Should -Throw Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly } - It 'still requires -Name or -Id even with -Protocol supplied' { - { Remove-PfbFileSystemSession -Protocol 'nfs' -Confirm:$false -Array $fakeArray } | Should -Throw + It 'rejects combining -Name and -Protocol (true mutual exclusivity, matching the server)' { + { Remove-PfbFileSystemSession -Name 'some-session-name' -Protocol 'nfs' -Confirm:$false -Array $fakeArray } | Should -Throw Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly } - It 'passes valid -Protocol values through to the query string, comma-joined, alongside -Name' { - Remove-PfbFileSystemSession -Name 'fs01' -Protocol 'nfs', 'smb' -Confirm:$false -Array $fakeArray + It 'terminates a single session by its own session name via -Name, sending only names' { + Remove-PfbFileSystemSession -Name '22517998136858346-smb' -Confirm:$false -Array $fakeArray Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { $Method -eq 'DELETE' -and $Endpoint -eq 'file-systems/sessions' -and - $QueryParams['names'] -eq 'fs01' -and $QueryParams['protocols'] -eq 'nfs,smb' + $QueryParams['names'] -eq '22517998136858346-smb' -and + -not $QueryParams.ContainsKey('protocols') -and -not $QueryParams.ContainsKey('disruptive') } } - It 'passes valid -Protocol values through to the query string alongside -Id' { - Remove-PfbFileSystemSession -Id 'abc-123' -Protocol 'smb' -Confirm:$false -Array $fakeArray + It 'bulk-terminates by -Protocol, sending protocols comma-joined plus the required disruptive flag' { + Remove-PfbFileSystemSession -Protocol 'nfs', 'smb' -Force -Confirm:$false -Array $fakeArray Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { $Method -eq 'DELETE' -and $Endpoint -eq 'file-systems/sessions' -and - $QueryParams['ids'] -eq 'abc-123' -and $QueryParams['protocols'] -eq 'smb' + $QueryParams['protocols'] -eq 'nfs,smb' -and $QueryParams['disruptive'] -eq 'true' -and + -not $QueryParams.ContainsKey('names') } } - It 'omits -Protocol from the query string when not specified' { - Remove-PfbFileSystemSession -Name 'fs01' -Confirm:$false -Array $fakeArray - Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { - -not $QueryParams.ContainsKey('protocols') - } + It 'requires -Force for the -Protocol bulk path, independent of $ConfirmPreference (rejects -Protocol without -Force even with -Confirm:$false)' { + { Remove-PfbFileSystemSession -Protocol 'smb' -Confirm:$false -Array $fakeArray } | Should -Throw + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly + } + + It 'honors -WhatIf for the -Name path (no call made)' { + Remove-PfbFileSystemSession -Name '22517998136858346-smb' -WhatIf -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly } - It 'honors -WhatIf (no call made)' { - Remove-PfbFileSystemSession -Name 'fs01' -Protocol 'nfs' -WhatIf -Array $fakeArray + It 'honors -WhatIf for the -Protocol bulk path (no call made)' { + Remove-PfbFileSystemSession -Protocol 'smb' -Force -WhatIf -Array $fakeArray Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 0 -Exactly } } From 9eec51214e267d75665815e0d20669102529a104 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Tue, 21 Jul 2026 13:37:59 -0700 Subject: [PATCH 9/9] Fix Get-PfbFileSystemSession: remove fake -Id, fix -Name help text Same underlying bug found while live-testing Remove-PfbFileSystemSession''s sibling cmdlet: there is no `ids` query parameter on GET file-systems/sessions in any spec version (2.10-2.27 checked) -- -Id was never functional, no session object has an "id" field. Removed the ById parameter set entirely. -Name''s help text incorrectly claimed to filter by "file system names" -- live-verified (via the same investigation) that it actually filters by the session''s own generated name (e.g. "22517998136858346-smb"), not a file system name. Fixed the doc text; the underlying `$queryParams[''names'']` wiring was already correct. Unlike the DELETE side, GET''s spec description does not document any mutual-exclusivity restriction between `names` and `protocols` -- Task 6''s -Protocol addition (prior commit) is unaffected and remains combinable with -Name here. Tests/Get-PfbFileSystemSession.Tests.ps1: added assertions that -Id no longer exists and that -Name passes through to the `names` query parameter correctly. --- .../FileSystem/Get-PfbFileSystemSession.ps1 | 19 ++++++------------- Tests/Get-PfbFileSystemSession.Tests.ps1 | 11 +++++++++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Public/FileSystem/Get-PfbFileSystemSession.ps1 b/Public/FileSystem/Get-PfbFileSystemSession.ps1 index ff4dfb3d..0c48f2cb 100644 --- a/Public/FileSystem/Get-PfbFileSystemSession.ps1 +++ b/Public/FileSystem/Get-PfbFileSystemSession.ps1 @@ -4,12 +4,12 @@ function Get-PfbFileSystemSession { Retrieves file system sessions from the FlashBlade. .DESCRIPTION Returns active client sessions connected to file systems on the FlashBlade. - Supports filtering by file system name, ID, or advanced filter expressions. + Supports filtering by session name, protocol, or advanced filter expressions. Auto-paginates by default. .PARAMETER Name - One or more file system names to retrieve sessions for. Accepts pipeline input. - .PARAMETER Id - One or more session IDs to retrieve. + One or more session names to filter by -- the session's own generated identifier + (e.g. as returned in a prior Get-PfbFileSystemSession call's Name property), NOT the + name of a file system. Accepts pipeline input. .PARAMETER Filter A server-side filter expression to narrow results. .PARAMETER Sort @@ -27,8 +27,8 @@ function Get-PfbFileSystemSession { Get-PfbFileSystemSession Returns all file system sessions on the FlashBlade. .EXAMPLE - Get-PfbFileSystemSession -Name "fs01" - Returns all sessions connected to file system 'fs01'. + Get-PfbFileSystemSession -Name "22517998136858346-smb" + Returns the single session with that session name. .EXAMPLE Get-PfbFileSystemSession -Filter "protocol='NFS'" -Limit 50 Returns up to 50 NFS sessions. @@ -39,10 +39,6 @@ function Get-PfbFileSystemSession { [ValidateNotNullOrEmpty()] [string[]]$Name, - [Parameter(ParameterSetName = 'ById')] - [ValidateNotNullOrEmpty()] - [string[]]$Id, - [Parameter()] [string]$Filter, @@ -67,18 +63,15 @@ function Get-PfbFileSystemSession { begin { Assert-PfbConnection -Array ([ref]$Array) $allNames = [System.Collections.Generic.List[string]]::new() - $allIds = [System.Collections.Generic.List[string]]::new() } process { if ($Name) { foreach ($n in $Name) { $allNames.Add($n) } } - if ($Id) { foreach ($i in $Id) { $allIds.Add($i) } } } end { $queryParams = @{} if ($allNames.Count -gt 0) { $queryParams['names'] = $allNames -join ',' } - if ($allIds.Count -gt 0) { $queryParams['ids'] = $allIds -join ',' } if ($Filter) { $queryParams['filter'] = $Filter } if ($Sort) { $queryParams['sort'] = $Sort } if ($Limit -gt 0) { $queryParams['limit'] = $Limit } diff --git a/Tests/Get-PfbFileSystemSession.Tests.ps1 b/Tests/Get-PfbFileSystemSession.Tests.ps1 index cc429cc4..a0e0237b 100644 --- a/Tests/Get-PfbFileSystemSession.Tests.ps1 +++ b/Tests/Get-PfbFileSystemSession.Tests.ps1 @@ -36,4 +36,15 @@ Describe 'Get-PfbFileSystemSession' { -not $QueryParams.ContainsKey('protocols') } } + + It 'has no -Id parameter (the endpoint has no ids query parameter in any spec version)' { + (Get-Command Get-PfbFileSystemSession).Parameters.ContainsKey('Id') | Should -BeFalse + } + + It 'passes -Name through to the query string as the session''s own name filter' { + Get-PfbFileSystemSession -Name '22517998136858346-smb' -Array $fakeArray + Should -Invoke Invoke-PfbApiRequest -ModuleName PureStorageFlashBladePowerShell -Times 1 -Exactly -ParameterFilter { + $QueryParams['names'] -eq '22517998136858346-smb' + } + } }